diff options
author | Miroslav Grepl <mgrepl@redhat.com> | 2014-04-11 09:37:53 +0200 |
---|---|---|
committer | Miroslav Grepl <mgrepl@redhat.com> | 2014-04-11 09:37:53 +0200 |
commit | 47be9ff57e72906660bb62a515222f482131e1fb (patch) | |
tree | 2cb0ef0ba48d73b1df7cc0915754a17e19464bb6 /libapol | |
download | setools-47be9ff57e72906660bb62a515222f482131e1fb.tar.gz setools-47be9ff57e72906660bb62a515222f482131e1fb.tar.xz setools-47be9ff57e72906660bb62a515222f482131e1fb.zip |
Create setools-3.3.7 git repomaster
Diffstat (limited to 'libapol')
99 files changed, 32048 insertions, 0 deletions
diff --git a/libapol/Makefile.am b/libapol/Makefile.am new file mode 100644 index 0000000..7bead2a --- /dev/null +++ b/libapol/Makefile.am @@ -0,0 +1,8 @@ +if DO_SWIGIFY + MAYBE_SWIG = swig +endif + +SUBDIRS = src include tests $(MAYBE_SWIG) + +libapol.a libapol.so: + $(MAKE) -C src $@ diff --git a/libapol/include/Makefile.am b/libapol/include/Makefile.am new file mode 100644 index 0000000..c3a820c --- /dev/null +++ b/libapol/include/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = apol diff --git a/libapol/include/apol/Makefile.am b/libapol/include/apol/Makefile.am new file mode 100644 index 0000000..0883c10 --- /dev/null +++ b/libapol/include/apol/Makefile.am @@ -0,0 +1,35 @@ +apoldir = $(includedir)/apol + +apol_HEADERS = \ + avrule-query.h \ + bool-query.h \ + bst.h \ + class-perm-query.h \ + condrule-query.h \ + constraint-query.h \ + context-query.h \ + domain-trans-analysis.h \ + fscon-query.h \ + infoflow-analysis.h \ + isid-query.h \ + mls-query.h \ + mls_level.h \ + mls_range.h \ + netcon-query.h \ + perm-map.h \ + permissive-query.h \ + polcap-query.h \ + policy.h \ + policy-path.h \ + policy-query.h \ + range_trans-query.h \ + rbacrule-query.h \ + relabel-analysis.h \ + render.h \ + role-query.h \ + terule-query.h \ + type-query.h \ + types-relation-analysis.h \ + user-query.h \ + util.h \ + vector.h diff --git a/libapol/include/apol/avrule-query.h b/libapol/include/apol/avrule-query.h new file mode 100644 index 0000000..1f4b072 --- /dev/null +++ b/libapol/include/apol/avrule-query.h @@ -0,0 +1,373 @@ +/** + * @file + * + * Routines to query access vector rules of a policy. These are + * allow, neverallow, auditallow, and dontaudit rules. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_AVRULE_QUERY_H +#define APOL_AVRULE_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + + typedef struct apol_avrule_query apol_avrule_query_t; + +/** + * Execute a query against all access vector rules within the policy. + * + * @param p Policy within which to look up avrules. + * @param a Structure containing parameters for query. If this is + * NULL then return all avrules. + * @param v Reference to a vector of qpol_avrule_t. The vector will + * be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_avrule_get_by_query(const apol_policy_t * p, const apol_avrule_query_t * a, apol_vector_t ** v); + +/** + * Execute a query against all syntactic access vector rules within + * the policy. If the policy has line numbers, then the returned list + * + * @param p Policy within which to look up avrules. The policy must + * be capable of having syntactic rules. + * @param a Structure containing parameters for query. If this is + * NULL then return all avrules. + * @param v Reference to a vector of qpol_syn_avrule_t. The vector + * will be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_syn_avrule_get_by_query(const apol_policy_t * p, const apol_avrule_query_t * a, apol_vector_t ** v); + +/** + * Allocate and return a new avrule query structure. All fields are + * initialized, such that running this blank query results in + * returning all avrules within the policy. The caller must call + * apol_avrule_query_destroy() upon the return value afterwards. + * + * @return An initialized avrule query structure, or NULL upon error. + */ + extern apol_avrule_query_t *apol_avrule_query_create(void); + +/** + * Deallocate all memory associated with the referenced avrule query, + * and then set it to NULL. This function does nothing if the query + * is already NULL. + * + * @param a Reference to a avrule query structure to destroy. + */ + extern void apol_avrule_query_destroy(apol_avrule_query_t ** a); + +/** + * Set an avrule query to search only certain access vector rules + * within the policy. This is a bitmap; use the constants in + * libqpol/avrule_query.h (QPOL_RULE_ALLOW, etc.) to give the rule + * selections. + * + * @param p Policy handler, to report errors. + * @param a AV rule query to set. + * @param rules Bitmap to indicate which rules to search, or 0 to + * search all rules. + * + * @return Always 0. + */ + extern int apol_avrule_query_set_rules(const apol_policy_t * p, apol_avrule_query_t * a, unsigned int rules); + +/** + * Set an avrule query to return rules whose source symbol matches + * symbol. Symbol may be a type or attribute; if it is an alias then + * the query will convert it to its primary prior to searching. If + * is_indirect is non-zero then the search will be done indirectly. + * If the symbol is a type, then the query matches rules with one of + * the type's attributes. If the symbol is an attribute, then it + * matches rule with any of the attribute's types. + * + * @param p Policy handler, to report errors. + * @param a AV rule query to set. + * @param symbol Limit query to rules with this symbol as their + * source, or NULL to unset this field. + * @param is_indirect If non-zero, perform indirect matching. + * + * @return 0 on success, negative on error. + */ + extern int apol_avrule_query_set_source(const apol_policy_t * p, apol_avrule_query_t * a, const char *symbol, + int is_indirect); + +/** + * Set an avrule query to return rules whose source symbol is matched as a type + * or an attribute. The symbol will match both types and attributes by default. + * @see apol_avrule_query_set_source() to set the symbol to match. + * + * @param p Policy handler, to report errors. + * @param a AV rule query to set. + * @param component Bit-wise or'ed set of APOL_QUERY_SYMBOL_IS_TYPE + * and APOL_QUERY_SYMBOL_IS_ATTRIBUTE indicating the type of component + * to match. + * + * @return 0 on success, negative on error. + */ + extern int apol_avrule_query_set_source_component(const apol_policy_t * p, apol_avrule_query_t * a, unsigned int component); + +/** + * Set an avrule query to return rules whose target symbol matches + * symbol. Symbol may be a type or attribute; if it is an alias then + * the query will convert it to its primary prior to searching. If + * is_indirect is non-zero then the search will be done indirectly. + * If the symbol is a type, then the query matches rules with one of + * the type's attributes. If the symbol is an attribute, then it + * matches rule with any of the attribute's types. + * + * @param p Policy handler, to report errors. + * @param a AV rule query to set. + * @param symbol Limit query to rules with this symbol as their + * target, or NULL to unset this field. + * @param is_indirect If non-zero, perform indirect matching. + * + * @return 0 on success, negative on error. + */ + extern int apol_avrule_query_set_target(const apol_policy_t * p, apol_avrule_query_t * a, const char *symbol, + int is_indirect); + +/** + * Set an avrule query to return rules whose target symbol is matched as a type + * or an attribute. The symbol will match both types and attributes by default. + * @see apol_avrule_query_set_target() to set the symbol to match. + * + * @param p Policy handler, to report errors. + * @param a AV rule query to set. + * @param component Bit-wise or'ed set of APOL_QUERY_SYMBOL_IS_TYPE + * and APOL_QUERY_SYMBOL_IS_ATTRIBUTE indicating the type of component + * to match. + * + * @return 0 on success, negative on error. + */ + extern int apol_avrule_query_set_target_component(const apol_policy_t * p, apol_avrule_query_t * a, unsigned int component); + +/** + * Set an avrule query to return rules with this object (non-common) + * class. If more than one class are appended to the query, the + * rule's class must be one of those appended. (I.e., the rule's + * class must be a member of the query's classes.) Pass a NULL to + * clear all classes. Note that this performs straight string + * comparison, ignoring the regex flag. + * + * @param p Policy handler, to report errors. + * @param a AV rule query to set. + * @param obj_class Name of object class to add to search set, or NULL + * to clear all classes. + * + * @return 0 on success, negative on error. + */ + extern int apol_avrule_query_append_class(const apol_policy_t * p, apol_avrule_query_t * a, const char *obj_class); + +/** + * Set an avrule query to return rules with this permission. By + * default, if more than one permission are appended to the query, at + * least one of the rule's permissions must be one of those appended; + * that is, the intersection of query's and rule's permissions must be + * non-empty. (This behavior can be changed.) Pass a NULL to clear + * all permissions. Note that this performs a straight string + * comparison, ignoring the regex flag. + * + * @param p Policy handler, to report errors. + * @param a AV rule query to set. + * @param perm Name of permission to add to search set, or NULL to + * clear all permissions. + * + * @return 0 on success, negative on error. + * + * @see apol_avrule_query_set_all_perms() + */ + extern int apol_avrule_query_append_perm(const apol_policy_t * p, apol_avrule_query_t * a, const char *perm); + +/** + * Set an avrule query to return rules that are in conditionals and + * whose conditional uses a particular boolean variable. + * Unconditional rules will not be returned. + * + * @param p Policy handler, to report errors. + * @param a AV rule query to set. + * @param bool_name Name of boolean that conditional must contain. If + * NULL then search all rules. + * + * @return 0 on success, negative on error. + */ + extern int apol_avrule_query_set_bool(const apol_policy_t * p, apol_avrule_query_t * a, const char *bool_name); + +/** + * Set an avrule query to search only enabled rules within the policy. + * These include rules that are unconditional and those within enabled + * conditionals. + * + * @param p Policy handler, to report errors. + * @param a AV rule query to set. + * @param is_enabled Non-zero to search only enabled rules, 0 to + * search all rules. + * + * @return Always 0. + */ + extern int apol_avrule_query_set_enabled(const apol_policy_t * p, apol_avrule_query_t * a, int is_enabled); + +/** + * Normally, if more than one permission are added to the query then + * all returned rules will have <em>at least one</em> of those + * permissions. If the all_perms flag is set, then returned rules + * will have <em>all</em> of the given permissions. This flag does + * nothing if no permissions are given. + * + * <em>Note:</em> If calling apol_syn_avrule_get_by_query(), the + * returned results may not be what is expected. For a given + * source-target-class triplet, all of the associated permissions are + * unioned together prior to executing the avrule query. Although a + * given syntactic AV rule might not have all of the matched + * permissions, the union of the rules' permissions will them. For + * example, consider these two allow rules: + * + *<pre>allow A B : C p1; + *allow A B : C p2;</pre> + * + * If the avrule query has both permissions p1 and p2 and the + * all_perms flag is set, then both of these syntactic rules will be + * returned by apol_syn_avrule_get_by_query(). + * + * @param p Policy handler, to report errors. + * @param a AV rule query to set. + * @param all_perms Non-zero to match all permissions, zero to match + * any permission. + * + * @return Always 0. + * + * @see apol_avrule_query_append_perm() + */ + extern int apol_avrule_query_set_all_perms(const apol_policy_t * p, apol_avrule_query_t * a, int all_perms); + +/** + * Set an avrule query to treat the source symbol as any. That is, + * use the same symbol for either source or target of a rule. This + * flag does nothing if the source symbol is not set. + * + * @param p Policy handler, to report errors. + * @param a AV rule query to set. + * @param is_any Non-zero to use source symbol for any field, 0 to + * keep source as only source. + * + * @return Always 0. + */ + extern int apol_avrule_query_set_source_any(const apol_policy_t * p, apol_avrule_query_t * a, int is_any); + +/** + * Set an avrule query to use regular expression searching for source + * and target types/attributes. Strings will be treated as regexes + * instead of literals. Matching will occur against the type name or + * any of its aliases. + * + * @param p Policy handler, to report errors. + * @param a AV rule query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_avrule_query_set_regex(const apol_policy_t * p, apol_avrule_query_t * a, int is_regex); + +/** + * Given a single avrule, return a newly allocated vector of + * qpol_syn_avrule_t pointers (relative to the given policy) which + * comprise that rule. The vector will be sorted by line numbers if + * the policy has line numbers. If the given perms vector is non-NULL + * and non-empty, then only return syntactic rules with at least one + * permission listed within the perms vector. + * + * @param p Policy from which to obtain syntactic rules. + * @param rule AV rule to convert. + * @param perms If non-NULL and non-empty, a list of permission + * strings. Returned syn avrules will have at least one permission in + * common with this list. + * + * @return A newly allocated vector of syn_avrule_t pointers. The + * caller is responsible for calling apol_vector_destroy() afterwards. + */ + extern apol_vector_t *apol_avrule_to_syn_avrules(const apol_policy_t * p, const qpol_avrule_t * rule, + const apol_vector_t * perms); + +/** + * Given a vector of avrules (qpol_avrule_t pointers), return a newly + * allocated vector of qpol_syn_avrule_t pointers (relative to the + * given policy) which comprise all of those rules. The returned + * vector will be sorted by line numbers if the policy has line + * numbers. Also, it will not have any duplicate syntactic rules. If + * the given perms vector is non-NULL and non-empty, then only return + * syntactic rules with at least one permission listed within the + * perms vector. + * + * @param p Policy from which to obtain syntactic rules. + * @param rules Vector of AV rules to convert. + * @param perms If non-NULL and non-empty, a list of permission + * strings. Returned syn avrules will have at least one permission in + * common with this list. + * + * @return A newly allocated vector of syn_avrule_t pointers. The + * caller is responsible for calling apol_vector_destroy() afterwards. + */ + extern apol_vector_t *apol_avrule_list_to_syn_avrules(const apol_policy_t * p, const apol_vector_t * rules, + const apol_vector_t * perms); + +/** + * Render an avrule to a string. + * + * @param policy Policy handler, to report errors. + * @param rule The rule to render. + * + * @return a newly malloc()'d string representation of the rule, or NULL on + * failure; if the call fails, errno will be set. The caller is responsible + * for calling free() on the returned string. + */ + extern char *apol_avrule_render(const apol_policy_t * policy, const qpol_avrule_t * rule); + +/** + * Render a syntactic avrule to a string. + * + * @param policy Policy handler to report errors. + * @param rule The rule to render. + * + * @return a newly malloc()'d string representation of the rule, or NULL on + * failure; if the call fails, errno will be set. The caller is responsible + * for calling free() on the returned string. +*/ + extern char *apol_syn_avrule_render(const apol_policy_t * policy, const qpol_syn_avrule_t * rule); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/bool-query.h b/libapol/include/apol/bool-query.h new file mode 100644 index 0000000..a735eec --- /dev/null +++ b/libapol/include/apol/bool-query.h @@ -0,0 +1,105 @@ +/** + * @file + * Public Interface for querying conditional booleans of a policy. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_BOOL_QUERY_H +#define APOL_BOOL_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + + typedef struct apol_bool_query apol_bool_query_t; + +/******************** booleans queries ********************/ + +/** + * Execute a query against all booleans within the policy. + * + * @param p Policy within which to look up booleans. + * @param b Structure containing parameters for query. If this is + * NULL then return all booleans. + * @param v Reference to a vector of qpol_bool_t. The vector will be + * allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_bool_get_by_query(const apol_policy_t * p, apol_bool_query_t * b, apol_vector_t ** v); + +/** + * Allocate and return a new boolean query structure. All fields are + * initialized, such that running this blank query results in + * returning all booleans within the policy. The caller must call + * apol_bool_query_destroy() upon the return value afterwards. + * + * @return An initialized boolean query structure, or NULL upon error. + */ + extern apol_bool_query_t *apol_bool_query_create(void); + +/** + * Deallocate all memory associated with the referenced boolean query, + * and then set it to NULL. This function does nothing if the query + * is already NULL. + * + * @param b Reference to a boolean query structure to destroy. + */ + extern void apol_bool_query_destroy(apol_bool_query_t ** b); + +/** + * Set a boolean query to return only booleans that match this name. + * This function duplicates the incoming name. + * + * @param p Policy handler, to report errors. + * @param b Boolean query to set. + * @param name Limit query to only booleans with this name, or NULL to + * unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_bool_query_set_bool(const apol_policy_t * p, apol_bool_query_t * b, const char *name); + +/** + * Set a boolean query to use regular expression searching for all of + * its fields. Strings will be treated as regexes instead of + * literals. + * + * @param p Policy handler, to report errors. + * @param b Boolean query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_bool_query_set_regex(const apol_policy_t * p, apol_bool_query_t * b, int is_regex); + +#ifdef __cplusplus +} +#endif + +#endif /* APOL_BOOL_QUERY_H */ diff --git a/libapol/include/apol/bst.h b/libapol/include/apol/bst.h new file mode 100644 index 0000000..ca21a76 --- /dev/null +++ b/libapol/include/apol/bst.h @@ -0,0 +1,178 @@ +/** + * @file + * Contains the API for a binary search tree. The tree guarantees + * uniqueness of all entries within. Note that BST functions are not + * thread-safe. Use this if you need uniqueness in items; use + * vectors otherwise because they are faster. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_BST_H +#define APOL_BST_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <stdlib.h> + + typedef struct apol_bst apol_bst_t; + + typedef int (apol_bst_comp_func) (const void *a, const void *b, void *data); + typedef void (apol_bst_free_func) (void *elem); + +#include "vector.h" + +/** + * Allocate and initialize an empty binary search tree. The tree + * must have a comparison function, used when comparing nodes so as + * to determine how to sort them. + * + * @param cmp A comparison call back for the type of element stored + * in the BST. The expected return value from this function is less + * than, equal to, or greater than 0 if the first argument is less + * than, equal to, or greater than the second respectively. If this + * is NULL then do pointer address comparison. + * @param fr Function to call when destroying the tree. Each + * element of the tree will be passed into this function; it should + * free the memory used by that element. If this parameter is NULL, + * the elements will not be freed. + * + * @return A pointer to a newly created BST on success and NULL on + * failure. If the call fails, errno will be set. The caller is + * responsible for calling apol_bst_destroy() to free memory used. + */ + extern apol_bst_t *apol_bst_create(apol_bst_comp_func * cmp, apol_bst_free_func * fr); + +/** + * Free a BST and any memory used by it. This will recursively + * invoke the free function that was stored within the tree when it + * was created. + * + * @param b Pointer to the BST to free. The pointer will be set to + * NULL afterwards. If already NULL then this function does nothing. + */ + extern void apol_bst_destroy(apol_bst_t ** b); + +/** + * Allocate and return a vector that has been initialized with the + * contents of a binary search tree. If change_owner is zero then + * this function will make a <b>shallow copy of the BST's + * contents</b>; the BST will still <em>own</em> the objects. + * Otherwise the victor will gain ownership of the items; the BST can + * then be destroyed safely without affecting the vector. (The + * resulting vector will be sorted as per the BST's comparison + * function.) + * + * @param b Binary search tree from which to copy. + * @param change_owner If zero then do a shallow copy, else change + * item ownership. + * + * @return A pointer to a newly created vector on success and NULL on + * failure. If the call fails, errno will be set. The caller is + * responsible for calling apol_vector_destroy() to free memory used + * by the vector. + */ + extern apol_vector_t *apol_bst_get_vector(apol_bst_t * b, int change_owner); + +/** + * Get the number of elements stored in the BST. + * + * @param b The BST from which to get the number of elements. Must + * be non-NULL. + * + * @return The number of elements in the BST; if b is NULL, return + * 0 and set errno. + */ + extern size_t apol_bst_get_size(const apol_bst_t * v); + +/** + * Find an element within a BST and return it. + * + * @param b The BST from which to get the element. + * @param elem The element to find. (This will be the second + * parameter to the comparison function given in apol_bst_create().) + * @param data Arbitrary data to pass as the comparison function's + * third paramater (the function given in apol_bst_create()). + * @param elem Location to write the found element. This value is + * undefined if the key did not match any elements. + * + * @return 0 if element was found, or < 0 if not found. + */ + extern int apol_bst_get_element(const apol_bst_t * b, const void *elem, void *data, void **result); + +/** + * Insert an element to the BST. If the element already exists then + * do not insert it again. + * + * @param b The BST to which to add the element. + * @param elem The element to add. + * @param data Arbitrary data to pass as the comparison function's + * third paramater (the function given in apol_bst_create()). + * + * @return 0 if the item was inserted, 1 if the item already exists + * (and thus not inserted). On failure return < 0, set errno, and b + * will be unchanged. + */ + extern int apol_bst_insert(apol_bst_t * b, void *elem, void *data); + +/** + * Insert an element into the BST, and then get the element back out. + * If the element did not already exist, then this function behaves + * the same as apol_bst_insert(). If however the element did exist, + * then the passed in element is freed (as per the BST's free + * function) and then the existing element is returned. + * + * @param b The BST to which to add the element. + * @param elem Reference to an element to add. If the element is + * new, then the pointer remains unchanged. Otherwise set the + * reference to the element already within the tree. + * @param data Arbitrary data to pass as the comparison function's + * third paramater (the function given in apol_bst_create()). + * + * @return 0 if the item was inserted, 1 if the item already exists + * (and thus not inserted). On failure return < 0, set errno, and b + * will be unchanged. + */ + extern int apol_bst_insert_and_get(apol_bst_t * b, void **elem, void *data); + +/** + * Map a function across all the elements of the BST. Mapping occurs in + * the sorted order as defined by the original comparison function. + * + * @param node BST upon which to map against. + * @param fn Function pointer that takes 2 arguments, first is a + * pointer to the data in a node of the BST, second is an arbitrary + * data element. The function may change the BST node, but it must + * not affect the node's sorting order within the tree. This function + * should return >= 0 on success; a return of < 0 signals error and + * ends the mapping over the tree. + + * @return Result of the last call to fn() (i.e., >= 0 on success < 0 on + * failure). If the tree is empty then return 0. + */ + extern int apol_bst_inorder_map(const apol_bst_t * b, int (*fn) (void *, void *), void *data); +#ifdef __cplusplus +} +#endif + +#endif /* APOL_BST_H */ diff --git a/libapol/include/apol/class-perm-query.h b/libapol/include/apol/class-perm-query.h new file mode 100644 index 0000000..abb2fbb --- /dev/null +++ b/libapol/include/apol/class-perm-query.h @@ -0,0 +1,255 @@ +/** + * @file + * + * Routines to query classes, commons, and permissions of a policy. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_CLASS_PERM_QUERY_H +#define APOL_CLASS_PERM_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + + typedef struct apol_class_query apol_class_query_t; + typedef struct apol_common_query apol_common_query_t; + typedef struct apol_perm_query apol_perm_query_t; + +/******************** object class queries ********************/ + +/** + * Execute a query against all classes within the policy. The results + * will only contain object classes, not common classes. + * + * @param p Policy within which to look up classes. + * @param c Structure containing parameters for query. If this is + * NULL then return all object classes. + * @param v Reference to a vector of qpol_class_t. The vector will be + * allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_class_get_by_query(const apol_policy_t * p, apol_class_query_t * c, apol_vector_t ** v); + +/** + * Allocate and return a new class query structure. All fields are + * initialized, such that running this blank query results in + * returning all object classes within the policy. The caller must + * call apol_class_query_destroy() upon the return value afterwards. + * + * @return An initialized class query structure, or NULL upon error. + */ + extern apol_class_query_t *apol_class_query_create(void); + +/** + * Deallocate all memory associated with the referenced class query, + * and then set it to NULL. This function does nothing if the query + * is already NULL. + * + * @param c Reference to a class query structure to destroy. + */ + extern void apol_class_query_destroy(apol_class_query_t ** c); + +/** + * Set a class query to return only object classes that match this + * name. This function duplicates the incoming name. + * + * @param p Policy handler, to report errors. + * @param c Class query to set. + * @param name Limit query to only classes with this name, or NULL + * to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_class_query_set_class(const apol_policy_t * p, apol_class_query_t * c, const char *name); + +/** + * Set a class query to return only object classes that inherit from a + * particular common class. Queries will not match classes without + * commons if this option is set. This function duplicates the + * incoming name. + * + * @param p Policy handler, to report errors. + * @param c Class query to set. + * @param name Limit query to only classes that inherit from this + * common class, or NULL to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_class_query_set_common(const apol_policy_t * p, apol_class_query_t * c, const char *name); + +/** + * Set a class query to use regular expression searching for all of + * its fields. Strings will be treated as regexes instead of + * literals. + * + * @param p Policy handler, to report errors. + * @param c Class query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_class_query_set_regex(const apol_policy_t * p, apol_class_query_t * c, int is_regex); + +/******************** common class queries ********************/ + +/** + * Execute a query against all common classes within the policy. The + * results will only contain common classes, not object classes. + * + * @param p Policy within which to look up common classes. + * @param c Structure containing parameters for query. If this is + * NULL then return all common classes. + * @param v Reference to a vector of qpol_common_t. The vector will + * be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_common_get_by_query(const apol_policy_t * p, apol_common_query_t * c, apol_vector_t ** v); + +/** + * Allocate and return a new common query structure. All fields are + * initialized, such that running this blank query results in + * returning all common classes within the policy. The caller must + * call apol_common_query_destroy() upon the return value afterwards. + * + * @return An initialized common query structure, or NULL upon error. + */ + extern apol_common_query_t *apol_common_query_create(void); + +/** + * Deallocate all memory associated with the referenced common query, + * and then set it to NULL. This function does nothing if the query + * is already NULL. + * + * @param c Reference to a common query structure to destroy. + */ + extern void apol_common_query_destroy(apol_common_query_t ** c); + +/** + * Set a common query to return only common classes that match this + * name. This function duplicates the incoming name. + * + * @param p Policy handler, to report errors. + * @param c Common query to set. + * @param name Limit query to only commons with this name, or NULL to + * unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_common_query_set_common(const apol_policy_t * p, apol_common_query_t * c, const char *name); + +/** + * Set a common query to use regular expression searching for all of + * its fields. Strings will be treated as regexes instead of + * literals. + * + * @param p Policy handler, to report errors. + * @param c Class query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_common_query_set_regex(const apol_policy_t * p, apol_common_query_t * c, int is_regex); + +/******************** permission queries ********************/ + +/** + * Execute a query against all permissions (both those declared in + * classes as well as commons) within the policy. The results will + * contain char pointers to permission names. Thus if the same + * permission name is declared within multiple classes (e.g., + * <tt>file/read</tt> and <tt>socket/read</tt>) then only one instance + * of <tt>read</tt> is returned. + * + * @param p Policy within which to look up permissions. + * @param pq Structure containing parameters for query. If this is + * NULL then return all permissions. + * @param v Reference to a vector of character pointers. The vector + * will be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_perm_get_by_query(const apol_policy_t * p, apol_perm_query_t * pq, apol_vector_t ** v); + +/** + * Allocate and return a new permission query structure. All fields + * are initialized, such that running this blank query results in + * returning all permissions within the policy. The caller must call + * apol_perm_query_destroy() upon the return value afterwards. + * + * @return An initialized permission query structure, or NULL upon + * error. + */ + extern apol_perm_query_t *apol_perm_query_create(void); + +/** + * Deallocate all memory associated with the referenced permission + * query, and then set it to NULL. This function does nothing if the + * query is already NULL. + * + * @param pq Reference to a permission query structure to destroy. + */ + extern void apol_perm_query_destroy(apol_perm_query_t ** pq); + +/** + * Set a permission query to return only permissions that match this + * name. This function duplicates the incoming name. + * + * @param p Policy handler, to report errors. + * @param pq Permission query to set. + * @param name Limit query to only permissions with this name, or NULL + * to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_perm_query_set_perm(const apol_policy_t * p, apol_perm_query_t * pq, const char *name); + +/** + * Set a permission query to use regular expression searching for all + * of its fields. Strings will be treated as regexes instead of + * literals. + * + * @param p Policy handler, to report errors. + * @param pq Permission query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_perm_query_set_regex(const apol_policy_t * p, apol_perm_query_t * pq, int is_regex); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/condrule-query.h b/libapol/include/apol/condrule-query.h new file mode 100644 index 0000000..16bbb14 --- /dev/null +++ b/libapol/include/apol/condrule-query.h @@ -0,0 +1,119 @@ +/** + * @file + * + * Routines to query conditional expressions and conditional rules of + * a policy. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_CONDRULE_QUERY_H +#define APOL_CONDRULE_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + + typedef struct apol_cond_query apol_cond_query_t; + +/** + * Execute a query against all conditional expressions within the + * policy. + * + * @param p Policy within which to look up expressions. + * @param c Structure containing parameters for query. If this is + * NULL then return all expressions. + * @param v Reference to a vector of qpol_cond_t. The vector will be + * allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_cond_get_by_query(const apol_policy_t * p, apol_cond_query_t * c, apol_vector_t ** v); + +/** + * Allocate and return a new cond query structure. All fields are + * initialized, such that running this blank query results in + * returning all conditional expressions within the policy. The + * caller must call apol_cond_query_destroy() upon the return value + * afterwards. + * + * @return An initialized cond query structure, or NULL upon error. + */ + extern apol_cond_query_t *apol_cond_query_create(void); + +/** + * Deallocate all memory associated with the referenced cond query, + * and then set it to NULL. This function does nothing if the query + * is already NULL. + * + * @param c Reference to a cond query structure to destroy. + */ + extern void apol_cond_query_destroy(apol_cond_query_t ** c); + +/** + * Set a cond query to search only conditional expressions that use a + * certain boolean variable. + * + * @param p Policy handler, to report errors. + * @param c Cond rule query to set. + * @param name Limit query to expressions with this boolean, or NULL + * to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_cond_query_set_bool(const apol_policy_t * p, apol_cond_query_t * c, const char *name); + +/** + * Set a cond query to use regular expression searching for all of its + * fields. Strings will be treated as regexes instead of literals. + * + * @param p Policy handler, to report errors. + * @param c Cond rule query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_cond_query_set_regex(const apol_policy_t * p, apol_cond_query_t * c, int is_regex); + +/** + * Given a conditional node, allocate and return a string + * representation of its conditional expression. + * + * @param p Policy handler, to report errors. + * @param cond Conditional node whose expression to render. + * + * @return A newly malloc()'d string representation of conditonal + * expression, or NULL on failure. The caller is responsible for + * calling free() on the returned string. + */ + extern char *apol_cond_expr_render(const apol_policy_t * p, const qpol_cond_t * cond); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/constraint-query.h b/libapol/include/apol/constraint-query.h new file mode 100644 index 0000000..9e5b077 --- /dev/null +++ b/libapol/include/apol/constraint-query.h @@ -0,0 +1,188 @@ +/** + * @file + * + * Routines to query constraint and validatetrans statements in a policy. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_CONSTRAINT_QUERY_H +#define APOL_CONSTRAINT_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" + + typedef struct apol_constraint_query apol_constraint_query_t; + typedef struct apol_validatetrans_query apol_validatetrans_query_t; + +/******************** constraint queries ********************/ + +/** + * Execute a query against all constraints within the policy. + * + * @param p Policy within which to look up constraints. + * @param c Structure containing parameters for query. If this is + * NULL then return all constraints. + * @param v Reference to a vector of qpol_constraint_t. The vector + * will be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_constraint_get_by_query(const apol_policy_t * p, apol_constraint_query_t * c, apol_vector_t ** v); + +/** + * Allocate and return a new constraint query structure. All fields + * are initialized, such that running this blank query results in + * returning all constraints within the policy. The caller must call + * apol_constraint_query_destroy() upon the return value afterwards. + * + * @return An initialized constraint query structure, or NULL upon + * error. + */ + extern apol_constraint_query_t *apol_constraint_query_create(void); + +/** + * Deallocate all memory associated with the referenced constraint + * query, and then set it to NULL. This function does nothing if the + * query is already NULL. + * + * @param c Reference to a constraint query structure to destroy. + */ + extern void apol_constraint_query_destroy(apol_constraint_query_t ** c); + +/** + * Set a constraint query to return only constraints that use object + * classes that match this name. This function duplicates the + * incoming name. + * + * @param p Policy handler, to report errors. + * @param c Constraint query to set. + * @param name Limit query to only classes with this name, or NULL + * to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_constraint_query_set_class(const apol_policy_t * p, apol_constraint_query_t * c, const char *name); + +/** + * Set a constraint query to return only constraints that employ + * permissions that match this name. This function duplicates the + * incoming name. + * + * @param p Policy handler, to report errors. + * @param c Constraint query to set. + * @param name Limit query to only permissions with this name, or NULL + * to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_constraint_query_set_perm(const apol_policy_t * p, apol_constraint_query_t * c, const char *name); + +/** + * Set a constraint query to use regular expression searching for all + * of its fields. Strings will be treated as regexes instead of + * literals. + * + * @param p Policy handler, to report errors. + * @param c Constraint query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_constraint_query_set_regex(const apol_policy_t * p, apol_constraint_query_t * c, int is_regex); + +/******************** validatetrans queries ********************/ + +/** + * Execute a query against all validatetrans statements within the + * policy. + * + * @param p Policy within which to look up validatetrans statements. + * @param vr Structure containing parameters for query. If this is + * NULL then return all validatetrans statements. + * @param v Reference to a vector of qpol_validatetrans_t. The vector + * will be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_validatetrans_get_by_query(const apol_policy_t * p, apol_validatetrans_query_t * vt, apol_vector_t ** v); + +/** + * Allocate and return a new validatetrans query structure. All + * fields are initialized, such that running this blank query results + * in returning all validatetrans within the policy. The caller must + * call apol_validatetrans_query_destroy() upon the return value + * afterwards. + * + * @return An initialized validatetrans query structure, or NULL upon + * error. + */ + extern apol_validatetrans_query_t *apol_validatetrans_query_create(void); + +/** + * Deallocate all memory associated with the referenced validatetrans + * query, and then set it to NULL. This function does nothing if the + * query is already NULL. + * + * @param vt Reference to a validatetrans query structure to destroy. + */ + extern void apol_validatetrans_query_destroy(apol_validatetrans_query_t ** vt); + +/** + * Set a validatetrans query to return only validatetrans that use + * object classes that match this name. This function duplicates the + * incoming name. + * + * @param p Policy handler, to report errors. + * @param vt Validatetrans query to set. + * @param name Limit query to only classes with this name, or NULL + * to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_validatetrans_query_set_class(const apol_policy_t * p, apol_validatetrans_query_t * vt, const char *name); + +/** + * Set a validatetrans query to use regular expression searching for + * all of its fields. Strings will be treated as regexes instead of + * literals. + * + * @param p Policy handler, to report errors. + * @param vt Validatetrans query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_validatetrans_query_set_regex(const apol_policy_t * p, apol_validatetrans_query_t * vt, int is_regex); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/context-query.h b/libapol/include/apol/context-query.h new file mode 100644 index 0000000..2ee269c --- /dev/null +++ b/libapol/include/apol/context-query.h @@ -0,0 +1,261 @@ +/** + * @file + * Public interface for querying and manipulating a context. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_CONTEXT_QUERY_H +#define APOL_CONTEXT_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "mls-query.h" +#include <qpol/policy.h> + + typedef struct apol_context apol_context_t; + +/** + * Allocate and return a new context structure. All fields are + * initialized to nothing. The caller must call + * apol_context_destroy() upon the return value afterwards. + * + * @return An initialized context structure, or NULL upon error. + */ + extern apol_context_t *apol_context_create(void); + +/** + * Allocate and return a new context structure, initialized from an + * existing qpol_context_t. The caller must call + * apol_context_destroy() upon the return value afterwards. + * + * @param p Policy from which the qpol_context_t was obtained. + * @param context The libqpol context for which to create a new apol + * context. This context will not be altered by this call. + * + * @return An initialized context structure, or NULL upon error. + */ + extern apol_context_t *apol_context_create_from_qpol_context(const apol_policy_t * p, const qpol_context_t * context); + +/** + * Take a literal context string that may be missing components (e.g., + * <b>user_u::type_t:s0:c0.c127</b>), fill in a newly allocated + * apol_context_t, and return it. If there is a MLS range component + * to the context, it will <b>not</b> expanded. The caller must call + * apol_context_destroy() upon the return value afterwards. + * + * Because this function creates a context without the benefit of a + * policy, its range is incomplete. Call apol_context_convert() to + * complete it. + * + * @param context_string Pointer to a string representing a (possibly + * incomplete) context, or NULL upon error. + * + * @return An initialized context structure, or NULL upon error. + */ + extern apol_context_t *apol_context_create_from_literal(const char *context_string); + +/** + * Deallocate all memory associated with a context structure and then + * set it to NULL. This function does nothing if the context is + * already NULL. + * + * @param context Reference to a context structure to destroy. + */ + extern void apol_context_destroy(apol_context_t ** context); + +/** + * Set the user field of a context structure. This function + * duplicates the incoming string. + * + * @param p Error reporting handler, or NULL to use default handler. + * @param context Context to modify. + * @param user New user field to set, or NULL to unset this field. + * + * @return 0 on success, < 0 on error. + */ + extern int apol_context_set_user(const apol_policy_t * p, apol_context_t * context, const char *user); + +/** + * Set the role field of a context structure. This function + * duplicates the incoming string. + * + * @param p Error reporting handler, or NULL to use default handler. + * @param context Context to modify. + * @param role New role field to set, or NULL to unset this field. + * + * @return 0 on success, < 0 on error. + */ + extern int apol_context_set_role(const apol_policy_t * p, apol_context_t * context, const char *role); + +/** + * Set the type field of a context structure. This function + * duplicates the incoming string. + * + * @param p Error reporting handler, or NULL to use default handler. + * @param context Context to modify. + * @param type New type field to set, or NULL to unset this field. + * + * @return 0 on success, < 0 on error. + */ + extern int apol_context_set_type(const apol_policy_t * p, apol_context_t * context, const char *type); + +/** + * Set the range field of a context structure. This function takes + * ownership of the range, such that the caller must not modify nor + * destroy it afterwards. + * + * @param p Error reporting handler, or NULL to use default handler. + * @param context Context to modify. + * @param range New range field to set, or NULL to unset this field. + * + * @return 0 on success, < 0 on error. + */ + extern int apol_context_set_range(const apol_policy_t * p, apol_context_t * context, apol_mls_range_t * range); + +/** + * Get the user field of a context structure. + * + * @param context Context to query. + * + * @return Context's user, or NULL if not set or upon error. Do not + * modify this string. + */ + extern const char *apol_context_get_user(const apol_context_t * context); + +/** + * Get the role field of a context structure. + * + * @param context Context to query. + * + * @return Context's role, or NULL if not set or upon error. Do not + * modify this string. + */ + extern const char *apol_context_get_role(const apol_context_t * context); + +/** + * Get the type field of a context structure. + * + * @param context Context to query. + * + * @return Context's type, or NULL if not set or upon error. Do not + * modify this string. + */ + extern const char *apol_context_get_type(const apol_context_t * context); + +/** + * Get the range field of a context structure. + * + * @param context Context to query. + * + * @return Context's range, or NULL if not set or upon error. Do not + * modify this structure. + */ + extern const apol_mls_range_t *apol_context_get_range(const apol_context_t * context); + +/** + * Compare two contexts, determining if one matches the other. The + * search context may have empty elements that indicate not to compare + * that field. Types will be matched if the two or any of their + * aliases are the same. The last parameter gives how to match ranges + * (assuming that search has a range); it must be one of + * APOL_QUERY_SUB, APOL_QUERY_SUPER, APOL_QUERY_EXACT or + * APOL_QUERY_INTERSECT as per apol_mls_range_compare(). If a context + * is not valid according to the policy then this function returns -1. + * If search is NULL then comparison always succeeds. + * + * @param p Policy within which to look up policy and MLS information. + * @param target Target context to compare. + * @param search Source context to compare. + * @param range_compare_type Specifies how to compare the ranges. + * + * @return 1 If comparison succeeds, 0 if not; -1 on error. + */ + extern int apol_context_compare(const apol_policy_t * p, + const apol_context_t * target, const apol_context_t * search, + unsigned int range_compare_type); + +/** + * Given a complete context (user, role, type, and range if policy is + * MLS), determine if it is legal according to the supplied policy. + * (Check that the user has that role, the role has that type, etc.) + * This function will convert from aliases to canonical forms as + * necessary. + * + * @param p Policy within which to look up context information. + * @param context Context to check. + * + * @return 1 If context is legal, 0 if not; -1 on error. + */ + extern int apol_context_validate(const apol_policy_t * p, const apol_context_t * context); + +/** + * Given a partial context, determine if it is legal according to the + * supplied policy. For fields that are not specified, assume that + * they would be legal. For example, if a user is given but not a + * role, then return truth if the user is in the policy. If the + * context is NULL then this function returns 1. This function will + * convert from aliases to canonical forms as necessary. + * + * @param p Policy within which to look up context information. + * @param context Context to check. + * + * @return 1 If context is legal, 0 if not; -1 on error. + */ + extern int apol_context_validate_partial(const apol_policy_t * p, const apol_context_t * context); + +/** + * Given a context, allocate and return a string that represents the + * context. This function does not check if the context is valid or + * not. An asterisk ("*") represents fields that have not been set. + * For example, if a context has the role object_r but has no user nor + * type set, it will be rendered as "<sample>*:object_r:*</sample>" + * (assuming the given policy is not MLS). + * + * @param p Policy within which to look up MLS range information. If + * NULL, then attempt to treat the range as incomplete. + * @param context Context to render. + * + * @return A newly allocated string on success, which the caller must + * free afterwards. Upon error return NULL. + */ + extern char *apol_context_render(const apol_policy_t * p, const apol_context_t * context); + +/** + * Given a context, convert the range within it (as per + * apol_mls_range_convert()) to a complete range. If the context has + * no range or has no literal range then do nothing. + * + * @param p Policy containing category information. + * @param context Context to convert. + * + * @return 0 on success, < 0 on error. + */ + extern int apol_context_convert(const apol_policy_t * p, apol_context_t * context); + +#ifdef __cplusplus +} +#endif + +#endif /* APOL_CONTEXT_QUERY_H */ diff --git a/libapol/include/apol/domain-trans-analysis.h b/libapol/include/apol/domain-trans-analysis.h new file mode 100644 index 0000000..2e05748 --- /dev/null +++ b/libapol/include/apol/domain-trans-analysis.h @@ -0,0 +1,427 @@ +/** + * @file + * + * Routines to perform a domain transition analysis. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_DOMAIN_TRANS_ANALYSIS_H +#define APOL_DOMAIN_TRANS_ANALYSIS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + + typedef struct apol_domain_trans_analysis apol_domain_trans_analysis_t; + typedef struct apol_domain_trans_result apol_domain_trans_result_t; + +#define APOL_DOMAIN_TRANS_DIRECTION_FORWARD 0x01 +#define APOL_DOMAIN_TRANS_DIRECTION_REVERSE 0x02 + +#define APOL_DOMAIN_TRANS_SEARCH_VALID 0x01 +#define APOL_DOMAIN_TRANS_SEARCH_INVALID 0x02 +#define APOL_DOMAIN_TRANS_SEARCH_BOTH (APOL_DOMAIN_TRANS_SEARCH_VALID|APOL_DOMAIN_TRANS_SEARCH_INVALID) + +/******************* table operation functions ****************************/ + +/** + * Build the table of domain transitions for a policy if not already built. + * @param policy The policy for which to build the table; if the table + * already exists for this policy, nothing is done. + * @return 0 on success and < 0 on failure; if the call fails, + * errno will be set and the table will be destroyed. + */ + extern int apol_policy_build_domain_trans_table(apol_policy_t * policy); + +/** + * @deprecated Use apol_policy_build_domain_trans_table(). + */ + extern int apol_policy_domain_trans_table_build(apol_policy_t * policy) __attribute__ ((deprecated)); + +/** + * Reset the state of the domain transition table in a policy. This + * is needed because by default subsequent calls to + * apol_domain_trans_analysis_do() will not produce results generated + * in a previous call. If calls are to be considered independent or + * calls in a different direction are desired, call this function + * prior to apol_domain_trans_analysis_do(). If the table was not + * built yet then this function does nothing. + * + * @param policy Policy containing the table for which the state + * should be reset. + */ + extern void apol_policy_reset_domain_trans_table(apol_policy_t * policy); + +/** + * @deprecated Use apol_policy_reset_domain_trans_table(). + */ + extern void apol_domain_trans_table_reset(apol_policy_t * policy) __attribute__ ((deprecated)); + +/*************** functions to do domain transition anslysis ***************/ + +/** + * Allocate and return a new domain transition analysis structure. All + * fields are cleared; one must fill in the details of the analysis + * before running it. The caller must call apol_domain_trans_analysis_destroy() + * upon the return value afterwards. + * @return An initialized domain transition analysis structure, or NULL + * upon error; if an error occurs errno will be set. + */ + extern apol_domain_trans_analysis_t *apol_domain_trans_analysis_create(void); + +/** + * Deallocate all memory associated with the referenced domain transition + * analysis structure, and then set it to NULL. This function does nothing if + * the analysis is already NULL. + * @param dta Reference to a domain transition analysis structure to destroy. + */ + extern void apol_domain_trans_analysis_destroy(apol_domain_trans_analysis_t ** dta); + +/** + * Set the direction of the transitions with respect to the start type. + * Must be either APOL_DOMAIN_TRANS_DIRECTION_FORWARD + * or APOL_DOMAIN_TRANS_DIRECTION_REVERSE. + * @param policy Policy handler, to report errors. + * @param dta Domain transition analysis to set. + * @param direction The direction to analyze using one of the two values above. + * @return 0 on success, and < 0 on error; if the call fails, + * errno will be set and dta will be unchanged. + */ + extern int apol_domain_trans_analysis_set_direction(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + unsigned char direction); + +/** + * Set the analysis to search for transitions based upon whether they + * would be permitted. The value must be one of APOL_DOMAIN_TRANS_SEARCH_* + * defined above. The default for a newly created analysis is to search + * for only valid transitions (i.e. APOL_DOMAIN_TRANS_SEARCH_VALID). + * @param policy Policy handler, to report errors. + * @param dta Domain transition analysis to set. + * @param valid One of APOL_DOMAIN_TRANS_SEARCH_*. + * @return 0 on success, and < 0 on error; if the call fails, + * errno will be set and dta will be unchanged. + */ + extern int apol_domain_trans_analysis_set_valid(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + unsigned char valid); + +/** + * Set the analysis to begin searching using a given type. This function + * must be called prior to running the analysis. If a previous type + * was set, it will be free()'d first. + * @param policy Policy handler, to report errors. + * @param dta Domain transition analysis to set. + * @param type_name Name of the type from which to begin searching. + * Must be non-NULL. This string will be duplicated. + * @return 0 on success, and < 0 on error; if the call fails, + * errno will be set and dta will be unchanged. + */ + extern int apol_domain_trans_analysis_set_start_type(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + const char *type_name); + +/** + * Set the analysis to return only types matching a regular expression. + * Note that the regular expression will also match types' aliases. + * @param policy Policy handler, to report errors. + * @param dta Domain transition analysis to set. + * @param result Only return results matching this regular expression, or + * NULL to return all types. + * @return 0 on success, and < 0 on failure; if the call fails, + * errno will be set. + */ + extern int apol_domain_trans_analysis_set_result_regex(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + const char *regex); + +/** + * Set the analysis to return only types having access (via allow + * rules) to this type. <b>This is only valid for forward + * analysis.</b> If more than one type is appended to the query, the + * resulting type must have access to at least one of the appended + * types. Pass a NULL to clear all previously appended types. <b>If + * access types are appended, the caller must also call + * apol_domain_trans_analysis_append_class() at least once with + * a valid class and apol_domain_trans_analysis_append_perm() at + * least once with a valid permission.</b> + * @param policy Policy handler, to report errors. + * @param dta Domain transition analysis to set. + * @param type_name Type to which a result must have access. + * @return 0 on success, and < 0 on error; if the call fails, + * errno will be set and dta will be unchanged. + */ + extern int apol_domain_trans_analysis_append_access_type(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + const char *type_name); + +/** + * Set the analysis to return only types having access (via allow + * rules) to this class with the given permission. <b>This is only + * valid for forward analysis.</b> If more than one class is appended + * to the query, the resulting type must have access to at least one + * of the appended classes. If more than one permission is appended + * for the same class, the resulting type must have at least one of + * the appended permissions for that class. Pass a NULL to both + * strings to clear all previously appended classes and + * permissions. <b>If access classes and permissions are appended, + * the caller must also call + * apol_domain_trans_analysis_append_access_type() at least once with + * a valid type.</b> + * @param policy Policy handler, to report errors. + * @param dta Domain transition analysis to set. + * @param class_name The class to which a result must have access. + * @param perm_name The permission which a result must have + * for the given class. + * @return 0 on success, and < 0 on error; if the call fails, + * errno will be set and dta will be unchanged. + * @deprecated This function has been split into + * apol_domain_trans_analysis_append_class() and + * apol_domain_trans_analysis_append_perm() + */ + extern int apol_domain_trans_analysis_append_class_perm(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + const char *class_name, const char *perm_name) + __attribute__ ((deprecated)); + +/** + * Set the analysis to return only types having access (via allow + * rules) to this class. <b>This is only valid for forward + * analysis.</b> If more than one class is appended to the query, the + * resulting type must have access to at least one of the appended + * classes. Pass a NULL to clear all previously appended classes. + * <b>If access classes are appended, the caller must also call + * apol_domain_trans_analysis_append_access_type() at least once with + * a valid type and apol_domain_trans_analysis_append_perm() with a + * valid permission.</b> + * @param policy Policy handler, to report errors. + * @param dta Domain transition analysis to set. + * @param class_name The class to which a result must have access. + * @return 0 on success, and < 0 on error; if the call fails, + * errno will be set and dta will be unchanged. + */ + extern int apol_domain_trans_analysis_append_class(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + const char *class_name); + +/** + * Set the analysis to return only types having access (via allow + * rules) to this permission. <b>This is only valid for forward + * analysis.</b> If more than one permission is appended the + * resulting type must have at least one of the appended permissions. + * Pass a NULL to clear all previously appended permissions. <b>If + * access permissions are appended, the caller must also call + * apol_domain_trans_analysis_append_access_type() at least once with + * a valid type and apol_domain_trans_analysis_append_class() at + * least once with a valid class.</b> + * @param policy Policy handler, to report errors. + * @param dta Domain transition analysis to set. + * @param perm_name The permission which a result must have. + * @return 0 on success, and < 0 on error; if the call fails, + * errno will be set and dta will be unchanged. + */ + extern int apol_domain_trans_analysis_append_perm(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + const char *perm_name); + +/** + * Execute a domain transition analysis against a particular policy. + * @param policy Policy containing the table to use. + * @param dta A non-NULL structure containng parameters for analysis. + * @param results A reference pointer to a vector of + * apol_domain_trans_result_t. The vector will be allocated by this + * function. The caller must call apol_vector_destroy() + * afterwards. This will be set to NULL upon error. + * @return 0 on success and < 0 on failure; if the call fails, + * errno will be set and *results will be NULL. + * + * @see apol_policy_reset_domain_trans_table() + */ + extern int apol_domain_trans_analysis_do(apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + apol_vector_t ** results); + +/***************** functions for accessing results ************************/ + +/** + * Return the start type of the transition in an + * apol_domain_trans_result node. The caller should not free the + * returned pointer. If the transition in the node is not valid + * there may be no start type in which case NULL is returned. + * @param dtr Domain transition result node. + * @return Pointer to the start type of the transition. + */ + extern const qpol_type_t *apol_domain_trans_result_get_start_type(const apol_domain_trans_result_t * dtr); + +/** + * Return the entrypoint type of the transition in an + * apol_domain_trans_result node. The caller should not free the + * returned pointer. If the transition in the node is not valid + * there may be no entrypoint in which case NULL is returned. + * @param dtr Domain transition result node. + * @return Pointer to the entrypoint type of the transition. + */ + extern const qpol_type_t *apol_domain_trans_result_get_entrypoint_type(const apol_domain_trans_result_t * dtr); + +/** + * Return the end type of the transition in an apol_domain_trans_result + * node. The caller should not free the returned pointer. If the transition + * in the node is not valid there may be no end type in which case NULL + * is returned. + * @param dtr Domain transition result node. + * @return Pointer to the start type of the transition. + */ + extern const qpol_type_t *apol_domain_trans_result_get_end_type(const apol_domain_trans_result_t * dtr); + +/** + * Return the vector of process transition rules (qpol_avrule_t + * pointers) in an apol_domain_trans_result node. The caller should + * not free the returned pointer. If the transition is invalid then + * the returned vector will be empty. + * @param dtr Domain transition result node. + * @return Vector of qpol_avrule_t relative to the policy originally + * used to generate the results. + */ + extern const apol_vector_t *apol_domain_trans_result_get_proc_trans_rules(const apol_domain_trans_result_t * dtr); + +/** + * Return the vector of file entrypoint rules (qpol_avrule_t + * pointers) in an apol_domain_trans_result node. The caller should + * not free the returned pointer. If the transition is invalid then + * the returned vector will be empty. + * @return Vector of qpol_avrule_t relative to the policy originally + * used to generate the results. + */ + extern const apol_vector_t *apol_domain_trans_result_get_entrypoint_rules(const apol_domain_trans_result_t * dtr); + +/** + * Return the vector of file execute rules (qpol_avrule_t pointers) + * in an apol_domain_trans_result node. The caller should not free + * the returned pointer. If the transition is invalid then the + * returned vector will be empty. + * @return Vector of qpol_avrule_t relative to the policy originally + * used to generate the results. + */ + extern const apol_vector_t *apol_domain_trans_result_get_exec_rules(const apol_domain_trans_result_t * dtr); + +/** + * Return the vector of process setexec rules (qpol_avrule_t + * pointers) in an apol_domain_trans_result node. The caller should + * not free the returned pointer. For all policies of version 15 or + * later a transition requires either a setexec rule or a + * type_transition rule to be valid. Valid transitions may have + * both; if there is no rule, this function returns an empty vector. + * @param dtr Domain transition result node. + * @return Vector of qpol_avrule_t relative to the policy originally + * used to generate the results. + */ + extern const apol_vector_t *apol_domain_trans_result_get_setexec_rules(const apol_domain_trans_result_t * dtr); + +/** + * Return the vector of type_transition rules (qpol_terule_t + * pointers) in an apol_domain_trans_result node. The caller should + * not free the returned pointer. For all policies of version 15 or + * later a transition requires either a setexec rule or a + * type_transition rule to be valid. Valid transitions may have + * both; if there is no rule, this function returns an empty vector. + * @param dtr Domain transition result node. + * @return Vector of qpol_terule_t relative to the policy originally + * used to generate the results. + */ + extern const apol_vector_t *apol_domain_trans_result_get_type_trans_rules(const apol_domain_trans_result_t * dtr); + +/** + * Determine if the transition in an apol_domain_trans_result node is valid. + * @param dtr Domain transition result node. + * @return 0 if invalid and non-zero if valid. If dtr is NULL, returns 0. + */ + extern int apol_domain_trans_result_is_trans_valid(const apol_domain_trans_result_t * dtr); + +/** + * Return the vector of access rules which satisfied the access + * types, classes, and permissions specified in the query. This is a + * vector of qpol_avrule_t pointers. The caller <b>should not</b> + * call apol_vector_destroy() upon the returned vector. This vector + * is only populated if access criteria were specified in the + * analysis. + * + * @param dtr Domain transition result node. + * @return Pointer to a vector of rules relative to the policy originally + * used to generate the results. + */ + extern const apol_vector_t *apol_domain_trans_result_get_access_rules(const apol_domain_trans_result_t * dtr); + +/** + * Do a deep copy (i.e., a clone) of an apol_domain_trans_result_t + * object. The caller is responsible for calling + * apol_domain_trans_result_destroy() upon the returned value. + * + * @param result Pointer to a domain trans result structure to + * destroy. + * + * @return A clone of the passed in result node, or NULL upon error. + */ + extern apol_domain_trans_result_t *apol_domain_trans_result_create_from_domain_trans_result(const apol_domain_trans_result_t + * in); + +/** + * Free all memory used by an apol_domain_trans_result_t object and + * set it to NULL. This does nothing if the pointer is already NULL. + * <b>This should only be called for results created by + * apol_domain_trans_result_create_from_domain_trans_result() and not + * those returned from within vectors.</b> + * + * @param res Reference pointer to a result to destroy. + */ + extern void apol_domain_trans_result_destroy(apol_domain_trans_result_t ** res); + +/************************ utility functions *******************************/ +/* define the following for rule type */ +#define APOL_DOMAIN_TRANS_RULE_PROC_TRANS 0x01 +#define APOL_DOMAIN_TRANS_RULE_EXEC 0x02 +#define APOL_DOMAIN_TRANS_RULE_EXEC_NO_TRANS 0x04 +#define APOL_DOMAIN_TRANS_RULE_ENTRYPOINT 0x08 +#define APOL_DOMAIN_TRANS_RULE_TYPE_TRANS 0x10 +#define APOL_DOMAIN_TRANS_RULE_SETEXEC 0x20 + +/** + * Verify that a transition using the given three types is valid in the given + * policy. If not valid, return a value indicating the missing rules. If any + * type is NULL, rules that would contain that type are considered missing. A + * valid transition requires a process transition, an entrypoint, and an + * execute rule. If the policy is version 15 or later it also requires either + * a setexec rule or a type_transition rule. The value + * APOL_DOMAIN_TRANS_RULE_EXEC_NO_TRANS is not returned by this function. + * + * @param policy The policy containing the domain transition table to + * consult. Must be non-NULL. + * @param start_dom The starting domain of the transition. May be NULL. + * @param ep_type The entrypoint of the transition. May be NULL. + * @param end_dom The ending domain of the transition. May be NULL. + * + * @return 0 if the transition is valid, < 0 on error, or a bit-wise + * or'ed set of APOL_DOMAIN_TRANS_RULE_* from above (always > 0) + * representing the rules missing from the transition. + */ + extern int apol_domain_trans_table_verify_trans(apol_policy_t * policy, const qpol_type_t * start_dom, + const qpol_type_t * ep_type, const qpol_type_t * end_dom); + +#ifdef __cplusplus +} +#endif + +#endif /* APOL_DOMAIN_TRANS_ANALYSIS_H */ diff --git a/libapol/include/apol/fscon-query.h b/libapol/include/apol/fscon-query.h new file mode 100644 index 0000000..18d0920 --- /dev/null +++ b/libapol/include/apol/fscon-query.h @@ -0,0 +1,249 @@ +/** + * @file + * Public Interface for querying genfscons and fs_uses of a policy. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_FSCON_QUERY_H +#define APOL_FSCON_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include "context-query.h" +#include <string.h> +#include <qpol/policy.h> + + typedef struct apol_genfscon_query apol_genfscon_query_t; + typedef struct apol_fs_use_query apol_fs_use_query_t; + +/******************** genfscon queries ********************/ + +/** + * Execute a query against all genfscons within the policy. The + * returned genfscons will be unordered. + * + * @param p Policy within which to look up genfscons. + * @param g Structure containing parameters for query. If this is + * NULL then return all genfscons. + * @param v Reference to a vector of qpol_genfscon_t. The vector will + * be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_genfscon_get_by_query(const apol_policy_t * p, const apol_genfscon_query_t * g, apol_vector_t ** v); + +/** + * Allocate and return a new genfscon query structure. All fields are + * initialized, such that running this blank query results in + * returning all genfscons within the policy. The caller must call + * apol_genfscon_query_destroy() upon the return value afterwards. + * + * @return An initialized genfscon query structure, or NULL upon + * error. + */ + extern apol_genfscon_query_t *apol_genfscon_query_create(void); + +/** + * Deallocate all memory associated with the referenced genfscon + * query, and then set it to NULL. This function does nothing if the + * query is already NULL. + * + * @param g Reference to a genfscon query structure to destroy. + */ + extern void apol_genfscon_query_destroy(apol_genfscon_query_t ** g); + +/** + * Set a genfscon query to return only genfscons that act upon this + * filesystem. + * + * @param p Policy handler, to report errors. + * @param g Genfscon query to set. + * @param fs Limit query to only genfscons with this filesystem, or + * NULL to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_genfscon_query_set_filesystem(const apol_policy_t * p, apol_genfscon_query_t * g, const char *fs); + +/** + * Set a genfscon query to return only genfscons that act upon this + * relative path. If the path includes a trailing slash, the search + * will ingore that slash. + * + * @param p Policy handler, to report errors. + * @param g Genfscon query to set. + * @param path Limit query to only genfscons with this path, or NULL + * to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_genfscon_query_set_path(const apol_policy_t * p, apol_genfscon_query_t * g, const char *path); + +/** + * Set a genfscon query to return only genfscons that act upon this + * object class. + * + * @param p Policy handler, to report errors. + * @param g Genfscon query to set. + * @param class Limit query to only genfscons with this object class, + * which must be one of QPOL_CLASS_BLK_FILE, QPOL_CLASS_CHR_FILE, + * etc., or negative to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_genfscon_query_set_objclass(const apol_policy_t * p, apol_genfscon_query_t * g, int objclass); + +/** + * Set a genfscon query to return only genfscons matching a context. + * This function takes ownership of the context, such that the caller + * must not modify nor destroy it afterwards. + * + * @param p Policy handler, to report errors. + * @param g Genfscon query to set. + * @param context Limit query to only genfscons matching this context, + * or NULL to unset this field. + * @param range_match Specifies how to match the MLS range within the + * context. This must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or + * APOL_QUERY_EXACT. This parameter is ignored if context is NULL. + * + * @return Always returns 0. + */ + extern int apol_genfscon_query_set_context(const apol_policy_t * p, + apol_genfscon_query_t * g, apol_context_t * context, unsigned int range_match); + +/** + * Creates a string containing the textual representation of + * a genfscon type. + * @param p Reference to a policy. + * @param genfscon Reference to the genfscon statement to be rendered. + * + * @return A newly allocated string on success, caller must free; + * NULL on error. + */ + extern char *apol_genfscon_render(const apol_policy_t * p, const qpol_genfscon_t * genfscon); + +/******************** fs_use queries ********************/ + +/** + * Execute a query against all fs_uses within the policy. The + * returned fs_use statements will be unordered. + * + * @param p Policy within which to look up fs_use statements. + * @param f Structure containing parameters for query. If this is + * NULL then return all fs_use statements. + * @param v Reference to a vector of qpol_fs_use_t. The vector will + * be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_fs_use_get_by_query(const apol_policy_t * p, const apol_fs_use_query_t * f, apol_vector_t ** v); + +/** + * Allocate and return a new fs_use query structure. All fields are + * initialized, such that running this blank query results in + * returning all fs_use statements within the policy. The caller must + * call apol_fs_use_query_destroy() upon the return value afterwards. + * + * @return An initialized fs_use query structure, or NULL upon error. + */ + extern apol_fs_use_query_t *apol_fs_use_query_create(void); + +/** + * Deallocate all memory associated with the referenced fs_use query, + * and then set it to NULL. This function does nothing if the query + * is already NULL. + * + * @param f Reference to a fs_use query structure to destroy. + */ + extern void apol_fs_use_query_destroy(apol_fs_use_query_t ** f); + +/** + * Set a fs_use query to return only fs_use statements that act upon + * this filesystem. + * + * @param p Policy handler, to report errors. + * @param f fs_use query to set. + * @param fs Limit query to only fs_use statements with this + * filesystem, or NULL to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_fs_use_query_set_filesystem(const apol_policy_t * p, apol_fs_use_query_t * f, const char *fs); + +/** + * Set a fs_use query to return only fs_use statements with this + * behavior. + * + * @param p Policy handler, to report errors. + * @param f fs_use query to set. + * @param behavior Limit query to only fs_use statements with this + * object class, which must be one of QPOL_FS_USE_XATTR, + * QPOL_FS_USE_TRANS, etc., or negative to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_fs_use_query_set_behavior(const apol_policy_t * p, apol_fs_use_query_t * f, int behavior); + +/** + * Set a fs_use query to return only fs_use statements matching a + * context. This function takes ownership of the context, such that + * the caller must not modify nor destroy it afterwards. Note that if + * a context is set, then the resulting query will never return + * fs_use_psid statements. + * + * @param p Policy handler, to report errors. + * @param f fs_use query to set. + * @param context Limit query to only fs_use statements matching this + * context, or NULL to unset this field. + * @param range_match Specifies how to match the MLS range within the + * context. This must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or + * APOL_QUERY_EXACT. This parameter is ignored if context is NULL. + * + * @return Always returns 0. + */ + extern int apol_fs_use_query_set_context(const apol_policy_t * p, + apol_fs_use_query_t * f, apol_context_t * context, unsigned int range_match); + +/** + * Creates a string containing the textual representation of + * a fs_use type. + * @param p Reference to a policy. + * @param fsuse Reference to the fs_use statement to be rendered. + * + * @return A newly allocated string on success, caller must free; + * NULL on error. + */ + extern char *apol_fs_use_render(const apol_policy_t * p, const qpol_fs_use_t * fsuse); + +#ifdef __cplusplus +} +#endif + +#endif /* APOL_FSCON_QUERY_H */ diff --git a/libapol/include/apol/infoflow-analysis.h b/libapol/include/apol/infoflow-analysis.h new file mode 100644 index 0000000..61b00a9 --- /dev/null +++ b/libapol/include/apol/infoflow-analysis.h @@ -0,0 +1,387 @@ +/** + * @file + * + * Routines to perform an information flow analysis, both direct and + * transitive flows. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_INFOFLOW_ANALYSIS_H +#define APOL_INFOFLOW_ANALYSIS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + +/* + * Information flows can be either direct (A -> B) or transitive (A -> + * {stuff} -> B). + */ +#define APOL_INFOFLOW_MODE_DIRECT 0x01 +#define APOL_INFOFLOW_MODE_TRANS 0x02 + +/* + * All operations are mapped in either an information flow in or an + * information flow out (using the permission map). These defines are + * for the two flow directions plus flows in both or either direction + * for queries and query results. + */ +#define APOL_INFOFLOW_IN 0x01 +#define APOL_INFOFLOW_OUT 0x02 +#define APOL_INFOFLOW_BOTH (APOL_INFOFLOW_IN|APOL_INFOFLOW_OUT) +#define APOL_INFOFLOW_EITHER 0x04 + + typedef struct apol_infoflow_graph apol_infoflow_graph_t; + typedef struct apol_infoflow_analysis apol_infoflow_analysis_t; + typedef struct apol_infoflow_result apol_infoflow_result_t; + typedef struct apol_infoflow_step apol_infoflow_step_t; + +/** + * Deallocate all space associated with a particular information flow + * graph, including the pointer itself. Afterwards set the pointer to + * NULL. + * + * @param g Reference to an apol_infoflow_graph_t to destroy. + */ + extern void apol_infoflow_graph_destroy(apol_infoflow_graph_t ** g); + +/********** functions to do information flow analysis **********/ + +/** + * Execute an information flow analysis against a particular policy. + * The policy must have had a permission map loaded via + * apol_policy_open_permmap(), else this analysis will abort + * immediately. + * + * @param p Policy within which to look up allow rules. + * @param ia A non-NULL structure containing parameters for analysis. + * @param v Reference to a vector of apol_infoflow_result_t. The + * vector will be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon + * error. + * @param g Reference to the information flow graph constructed for + * the given infoflow analysis object. The graph will be allocated by + * this function; the caller is responsible for calling + * apol_infoflow_graph_destroy() afterwards. This will be set to NULL + * upon error. + * + * @return 0 on success, negative on error. + */ + extern int apol_infoflow_analysis_do(const apol_policy_t * p, + const apol_infoflow_analysis_t * ia, apol_vector_t ** v, apol_infoflow_graph_t ** g); + +/** + * Execute an information flow analysis against a particular policy + * and a pre-built information flow graph. The analysis will keep the + * same criteria that were used to build the graph, sans differing + * starting type. + * + * @param p Policy within which to look up allow rules. + * @param g Existing information flow graph to analyze. + * @param type New string from which to begin analysis. + * @param v Reference to a vector of apol_infoflow_result_t. The + * vector will be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success, negative on error. + */ + extern int apol_infoflow_analysis_do_more(const apol_policy_t * p, apol_infoflow_graph_t * g, const char *type, + apol_vector_t ** v); + +/** + * Prepare an existing transitive infoflow graph to do further + * searches upon two specific start and end types. The analysis is by + * way of a BFS with random restarts; thus each call to + * apol_infoflow_analysis_trans_further_next() may possibly return + * additional paths. This function is needed to prepare the pool of + * initial states for the search. + * + * @param p Policy from which infoflow rules derived. + * @param g Existing transitive infoflow graph. If it was already + * prepared then those values will be first destroyed. + * @param start_type String from which to begin further analysis. + * @param end_type String for target infoflow paths. + * + * @return 0 on success, < 0 on error. + */ + extern int apol_infoflow_analysis_trans_further_prepare(const apol_policy_t * p, + apol_infoflow_graph_t * g, const char *start_type, + const char *end_type); + +/** + * Find further transitive infoflow paths by way of a random restart. + * The infoflow graph must be first prepared by first calling + * apol_infoflow_analysis_trans_further_prepare(). This function will + * append to vector v any new results it finds. + * + * @param p Policy from which infoflow rules derived. + * @param g Prepared transitive infoflow graph. + * @param v Pointer to a vector of existing apol_infoflow_result_t + * pointers. If this functions finds additional unique results it + * will append them to this vector. If the pointer is NULL then this + * will allocate and return a new vector. It is the caller's + * responsibility to call apol_vector_destroy() afterwards. + * + * @return 0 on success, < 0 on error. + */ + extern int apol_infoflow_analysis_trans_further_next(const apol_policy_t * p, apol_infoflow_graph_t * g, + apol_vector_t ** v); + +/********** functions to create/modify an analysis object **********/ + +/** + * Allocate and return a new information analysis structure. All + * fields are cleared; one must fill in the details of the analysis + * before running it. The caller must call + * apol_infoflow_analysis_destroy() upon the return value afterwards. + * + * @return An initialized information flow analysis structure, or NULL + * upon error. + */ + extern apol_infoflow_analysis_t *apol_infoflow_analysis_create(void); + +/** + * Deallocate all memory associated with the referenced information + * flow analysis, and then set it to NULL. This function does nothing + * if the analysis is already NULL. + * + * @param ia Reference to an infoflow analysis structure to destroy. + */ + extern void apol_infoflow_analysis_destroy(apol_infoflow_analysis_t ** ia); + +/** + * Set an information flow analysis mode to be either direct or + * transitive. This must be one of the values + * APOL_INFOFLOW_MODE_DIRECT, or APOL_INFOFLOW_MODE_TRANS. This + * function must be called prior to running the analysis. + * + * @param p Policy handler, to report errors. + * @param ia Infoflow analysis to set. + * @param mode Analysis mode, either direct or transitive. + * + * @return 0 on success, negative on error. + */ + extern int apol_infoflow_analysis_set_mode(const apol_policy_t * p, apol_infoflow_analysis_t * ia, unsigned int mode); + +/** + * Set an information flow analysis to search in a specific direction. + * For direct infoflow analysis this must be one of the values + * APOL_INFOFLOW_IN, APOL_INFOFLOW_OUT, APOL_INFOFLOW_BOTH, or + * APOL_INFOFLOW_EITHER; transitive infoflow only permits the first + * two. This function must be called prior to running the analysis. + * + * @param p Policy handler, to report errors. + * @param ia Infoflow analysis to set. + * @param dir Direction to analyze, using one of the defines above. + * + * @return 0 on success, negative on error. + */ + extern int apol_infoflow_analysis_set_dir(const apol_policy_t * p, apol_infoflow_analysis_t * ia, unsigned int dir); + +/** + * Set an information flow analysis to begin searching using a given + * type. This function must be called prior to running the analysis. + * + * @param p Policy handler, to report errors. + * @param ia Infoflow anlysis to set. + * @param name Begin searching types with this non-NULL name. + * + * @return 0 on success, negative on error. + */ + extern int apol_infoflow_analysis_set_type(const apol_policy_t * p, apol_infoflow_analysis_t * ia, const char *name); + +/** + * Set an information flow analysis to return paths that only go + * through this intermediate type. If more than one type is appended + * to the analysis, every step of a return path will go through at + * least one of the types. These intermediate types are ignored when + * running a direct information flow analysis. + * + * @param policy Policy handler, to report errors. + * @param ia Infoflow analysis to set. + * @param type Intermediate type which a result must flow through. If + * NULL, then clear all existing intermediate types. (All paths will + * be returned.) + * @return 0 on success, negative on error. + */ + extern int apol_infoflow_analysis_append_intermediate(const apol_policy_t * p, apol_infoflow_analysis_t * ia, + const char *type); + +/** + * Set an information flow analysis to return only rules with this + * object (non-common) class and permission. If more than one + * class/perm pair is appended to the query, every rule's class and + * permissions must be one of those appended. (I.e., the rule will be + * a member of the analysis's class/perm pairs.) + * + * @param policy Policy handler, to report errors. + * @param ia Infoflow analysis to set. + * @param class_name The class to which a result must have access. If + * NULL, then accept all class/perm pairs. + * @param perm_name The permission which a result must have for the + * given class. This may be NULL if class_name is also NULL. + * @return 0 on success, negative on error. + */ + extern int apol_infoflow_analysis_append_class_perm(const apol_policy_t * p, + apol_infoflow_analysis_t * ia, const char *class_name, + const char *perm_name); + +/** + * Set an information flow analysis to return only rules with at least + * one permission whose weight is greater than or equal to the given + * minimum. Permission weights are retrieved from the currently + * loaded permission map. If the given minimum exceeds + * APOL_PERMMAP_MAX_WEIGHT it will be clamped to that value. + * + * @param policy Policy handler, to report errors. + * @param ia Infoflow analysis to set. + * @param min_weight Minimum weight for rules, or negative to accept + * all rules. + * @return Always 0. + */ + extern int apol_infoflow_analysis_set_min_weight(const apol_policy_t * p, apol_infoflow_analysis_t * ia, int min_weight); + +/** + * Set an information flow analysis to return only types matching a + * regular expression. Note that the regexp will also match types' + * aliases. + * + * @param p Policy handler, to report errors. + * @param ia Information flow anlysis to set. + * @param result Only return types matching this regular expression, or + * NULL to return all types + * + * @return 0 on success, negative on error. + */ + extern int apol_infoflow_analysis_set_result_regex(const apol_policy_t * p, apol_infoflow_analysis_t * ia, + const char *result); + +/*************** functions to access infoflow results ***************/ + +/** + * Return the direction of an information flow result. This will be + * one of APOL_INFOFLOW_IN, APOL_INFOFLOW_OUT, or APOL_INFOFLOW_BOTH. + * + * @param result Infoflow result from which to get direction. + * @return Direction of result or zero on error. + */ + extern unsigned int apol_infoflow_result_get_dir(const apol_infoflow_result_t * result); + +/** + * Return the start type of an information flow result. The caller + * should not free the returned pointer. + * + * @param result Infoflow result from which to get start type. + * @return Pointer to the start type of the infoflow or NULL on error. + */ + extern const qpol_type_t *apol_infoflow_result_get_start_type(const apol_infoflow_result_t * result); + +/** + * Return the end type of an information flow result. The caller + * should not free the returned pointer. + * + * @param result Infoflow result from which to get end type. + * @return Pointer to the end type of the infoflow or NULL on error. + */ + extern const qpol_type_t *apol_infoflow_result_get_end_type(const apol_infoflow_result_t * result); + +/** + * Return the length of an information flow result. This represents + * how easily information flows from the start to end type, where + * lower numbers are easier than higher numbers. This is dependent + * upon the weights assigned in the currently loaded permission map. + * + * @param result Infoflow result from which to get length. + * @return Length of result or zero on error. + */ + extern unsigned int apol_infoflow_result_get_length(const apol_infoflow_result_t * result); + +/** + * Return the vector of infoflow steps for a particular information + * flow result. This is a vector of apol_infoflow_step_t pointers. + * The caller <b>should not</b> call apol_vector_destroy() upon the + * returned vector. Note that for a direct infoflow analysis this + * vector will consist of exactly one step; for transitive analysis + * the vector will have multiple steps. + * + * @param result Infoflow result from which to get steps. + * + * @return Pointer to a vector of steps found between the result's + * start and end types or NULL on error. + */ + extern const apol_vector_t *apol_infoflow_result_get_steps(const apol_infoflow_result_t * result); + +/** + * Return the starting type for an information flow step. The caller + * should not free the returned pointer. + * + * @param step Infoflow step from which to get start type. + * @return Pointer to the start type for this infoflow step or NULL on error. + */ + extern const qpol_type_t *apol_infoflow_step_get_start_type(const apol_infoflow_step_t * step); + +/** + * Return the ending type for an information flow step. The caller + * should not free the returned pointer. + * + * @param step Infoflow step from which to get end type. + * @return Pointer to the start type for this infoflow step or NULL on error. + */ + extern const qpol_type_t *apol_infoflow_step_get_end_type(const apol_infoflow_step_t * step); + +/** + * Return the weight of an information flow step. For a direct + * infoflow analysis the weight is zero. For a transitive + * analysis this is an integer value that quantatizes the amount of + * information that could flow between the start and end types; it is + * based upon the currently opened permission map. It will be a value + * between APOL_PERMMAP_MIN_WEIGHT and APOL_PERMMAP_MAX_WEIGHT, + * inclusive. + * + * @param step Infoflow step from which to get weight. + * @return Weight of step or < 0 on error. + */ + extern int apol_infoflow_step_get_weight(const apol_infoflow_step_t * step); + +/** + * Return the vector of access rules for a particular information + * step. This is a vector of qpol_avrule_t pointers. The caller + * <b>should not</b> call apol_vector_destroy() upon the returned + * vector. + * + * @param step Infoflow flow step from which to get rules. + * + * @return Pointer to a vector of rules relative to the policy originally + * used to generate the results or NULL on error. + */ + extern const apol_vector_t *apol_infoflow_step_get_rules(const apol_infoflow_step_t * step); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/isid-query.h b/libapol/include/apol/isid-query.h new file mode 100644 index 0000000..6c66ae9 --- /dev/null +++ b/libapol/include/apol/isid-query.h @@ -0,0 +1,111 @@ +/** + * @file + * Public Interface for querying initial SIDs of a policy. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_ISID_QUERY_H +#define APOL_ISID_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include "context-query.h" +#include <qpol/policy.h> + + typedef struct apol_isid_query apol_isid_query_t; + +/******************** isid queries ********************/ + +/** + * Execute a query against all initial SIDs within the policy. The + * returned isids will be unordered. + * + * @param p Policy within which to look up initial SIDs. + * @param i Structure containing parameters for query. If this is + * NULL then return all isids. + * @param v Reference to a vector of qpol_isid_t. The vector will be + * allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_isid_get_by_query(const apol_policy_t * p, const apol_isid_query_t * i, apol_vector_t ** v); + +/** + * Allocate and return a new isid query structure. All fields are + * initialized, such that running this blank query results in + * returning all initial SIDs within the policy. The caller must call + * apol_isid_query_destroy() upon the return value afterwards. + * + * @return An initialized isid query structure, or NULL upon error. + */ + extern apol_isid_query_t *apol_isid_query_create(void); + +/** + * Deallocate all memory associated with the referenced isid query, + * and then set it to NULL. This function does nothing if the query + * is already NULL. + * + * @param i Reference to an isid query structure to destroy. + */ + extern void apol_isid_query_destroy(apol_isid_query_t ** i); + +/** + * Set an isid query to return only initial SIDs with this name. + * + * @param p Policy handler, to report errors. + * @param i isid query to set. + * @param name Limit query to only initial SIDs with this name, or + * NULL to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_isid_query_set_name(const apol_policy_t * p, apol_isid_query_t * i, const char *name); + +/** + * Set an isid query to return only initial SIDs matching a context. + * This function takes ownership of the context, such that the caller + * must not modify nor destroy it afterwards. + * + * @param p Policy handler, to report errors. + * @param i isid query to set. + * @param context Limit query to only initial SIDs matching this + * context, or NULL to unset this field. + * @param range_match Specifies how to match the MLS range within the + * context. This must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or + * APOL_QUERY_EXACT. This parameter is ignored if context is NULL. + * + * @return Always returns 0. + */ + extern int apol_isid_query_set_context(const apol_policy_t * p, + apol_isid_query_t * i, apol_context_t * context, unsigned int range_match); + +#ifdef __cplusplus +} +#endif + +#endif /* APOL_ISID_QUERY_H */ diff --git a/libapol/include/apol/mls-query.h b/libapol/include/apol/mls-query.h new file mode 100644 index 0000000..22ee916 --- /dev/null +++ b/libapol/include/apol/mls-query.h @@ -0,0 +1,228 @@ +/** + * @file + * Public interface for querying MLS components, and for + * sensitivities and categories within a policy. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_MLS_QUERY_H +#define APOL_MLS_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "mls_level.h" +#include "mls_range.h" +#include "vector.h" + + typedef struct apol_level_query apol_level_query_t; + typedef struct apol_cat_query apol_cat_query_t; + +/* MLS comparisons function will return one of the following on + success or -1 on error */ +#define APOL_MLS_EQ 0 +#define APOL_MLS_DOM 1 +#define APOL_MLS_DOMBY 2 +#define APOL_MLS_INCOMP 3 + +/** + * Determine if two sensitivities are actually the same. Either level + * or both could be using a sensitivity's alias, thus straight string + * comparison is not sufficient. + * + * @param p Policy within which to look up MLS information. + * @param sens1 First sensitivity to compare. + * @param sens2 Second sensitivity to compare. + * + * @return 1 If comparison succeeds, 0 if not; -1 on error. + */ + extern int apol_mls_sens_compare(const apol_policy_t * p, const char *sens1, const char *sens2); + +/** + * Determine if two categories are actually the same. Either category + * or both could be using a category's alias, thus straight string + * comparison is not sufficient. + * + * @param p Policy within which to look up MLS information. + * @param cat1 First category to compare. + * @param cat2 Second category to compare. + * + * @return 1 If comparison succeeds, 0 if not; -1 on error. + */ + extern int apol_mls_cats_compare(const apol_policy_t * p, const char *cat1, const char *cat2); + +/******************** level queries ********************/ + +/** + * Execute a query against all levels within the policy. The results + * will only contain levels, not sensitivity aliases. The returned + * levels will be unordered. + * + * @param p Policy within which to look up levels. + * @param l Structure containing parameters for query. If this is + * NULL then return all levels. + * @param v Reference to a vector of qpol_level_t. The vector will be + * allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon + * upon error. Note that the vector may be empty if the policy is + * not an MLS policy. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_level_get_by_query(const apol_policy_t * p, apol_level_query_t * l, apol_vector_t ** v); + +/** + * Allocate and return a new level query structure. All fields are + * initialized, such that running this blank query results in + * returning all levels within the policy. The caller must call + * apol_level_query_destroy() upon the return value afterwards. + * + * @return An initialized level query structure, or NULL upon error. + */ + extern apol_level_query_t *apol_level_query_create(void); + +/** + * Deallocate all memory associated with the referenced level query, + * and then set it to NULL. This function does nothing if the query + * is already NULL. + * + * @param l Reference to a level query structure to destroy. + */ + extern void apol_level_query_destroy(apol_level_query_t ** l); + +/** + * Set a level query to return only levels that match this name. The + * name may be either a sensitivity or one of its aliases. This + * function duplicates the incoming name. + * + * @param p Policy handler, to report errors. + * @param l Level query to set. + * @param name Limit query to only sensitivities or aliases with this + * name, or NULL to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_level_query_set_sens(const apol_policy_t * p, apol_level_query_t * l, const char *name); + +/** + * Set a level query to return only levels contain a particular + * category. The name may be either a category or one of its aliases. + * This function duplicates the incoming name. + * + * @param p Policy handler, to report errors. + * @param l Level query to set. + * @param name Limit query to levels containing this category or + * alias, or NULL to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_level_query_set_cat(const apol_policy_t * p, apol_level_query_t * l, const char *name); + +/** + * Set a level query to use regular expression searching for all of + * its fields. Strings will be treated as regexes instead of + * literals. Matching will occur against the sensitivity name or any + * of its aliases. + * + * @param p Policy handler, to report errors. + * @param l Level query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_level_query_set_regex(const apol_policy_t * p, apol_level_query_t * l, int is_regex); + +/******************** category queries ********************/ + +/** + * Execute a query against all categories within the policy. The + * results will only contain categories, not aliases. The returned + * categories will be unordered. + * + * @param p Policy within which to look up categories. + * @param c Structure containing parameters for query. If this is + * NULL then return all categories. + * @param v Reference to a vector of qpol_cat_t. The vector will be + * allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon + * upon error. Note that the vector could be empty if the policy is + * not an MLS policy. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_cat_get_by_query(const apol_policy_t * p, apol_cat_query_t * c, apol_vector_t ** v); + +/** + * Allocate and return a new category query structure. All fields are + * initialized, such that running this blank query results in + * returning all categories within the policy. The caller must call + * apol_cat_query_destroy() upon the return value afterwards. + * + * @return An initialized category query structure, or NULL upon + * error. + */ + extern apol_cat_query_t *apol_cat_query_create(void); + +/** + * Deallocate all memory associated with the referenced category + * query, and then set it to NULL. This function does nothing if the + * query is already NULL. + * + * @param c Reference to a category query structure to destroy. + */ + extern void apol_cat_query_destroy(apol_cat_query_t ** c); + +/** + * Set a category query to return only categories that match this + * name. The name may be either a category or one of its aliases. + * This function duplicates the incoming name. + * + * @param p Policy handler, to report errors. + * @param c Category query to set. + * @param name Limit query to only categories or aliases with this + * name, or NULL to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_cat_query_set_cat(const apol_policy_t * p, apol_cat_query_t * c, const char *name); + +/** + * Set a category query to use regular expression searching for all of + * its fields. Strings will be treated as regexes instead of literals. + * Matching will occur against the category name or any of its + * aliases. + * + * @param p Policy handler, to report errors. + * @param c Category query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_cat_query_set_regex(const apol_policy_t * p, apol_cat_query_t * c, int is_regex); + +#ifdef __cplusplus +} +#endif + +#endif /* APOL_MLS_QUERY_H */ diff --git a/libapol/include/apol/mls_level.h b/libapol/include/apol/mls_level.h new file mode 100644 index 0000000..12e86b4 --- /dev/null +++ b/libapol/include/apol/mls_level.h @@ -0,0 +1,261 @@ +/** + * @file + * Public interface for representing and manipulating an + * apol_mls_level object. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_MLS_LEVEL_H +#define APOL_MLS_LEVEL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + + typedef struct apol_mls_level apol_mls_level_t; + +/** + * Allocate and return a new MLS level structure. All fields are + * initialized to nothing. The caller must call + * apol_mls_level_destroy() upon the return value afterwards. + * + * @return An initialized MLS level structure, or NULL upon error. + */ + extern apol_mls_level_t *apol_mls_level_create(void); + +/** + * Allocate and return an MLS level structure, initialized by an + * existing apol_mls_level_t object. The caller must call + * apol_mls_level_destroy() upon the return value afterwards. + * + * @param level Level to copy. If NULL then the returned MLS level + * will be initialized to nothing. + * + * @return An initialized MLS level structure, or NULL upon error. + */ + extern apol_mls_level_t *apol_mls_level_create_from_mls_level(const apol_mls_level_t * level); + +/** + * Take a MLS level string (e.g., <b>S0:C0.C127</b>) and parse it. + * Fill in a newly allocated apol_mls_level_t and return it. This + * function needs a policy to resolve dots within categories. If the + * string represents an illegal level then return NULL. The caller + * must call apol_mls_level_destroy() upon the returned value + * afterwards. + * + * @param p Policy within which to validate mls_level_string. + * @param mls_level_string Pointer to a string representing a valid + * MLS level. + * + * @return A filled in MLS level structure, or NULL upon error. + */ + extern apol_mls_level_t *apol_mls_level_create_from_string(const apol_policy_t * p, const char *mls_level_string); + +/** + * Take a literal MLS level string (e.g., <b>S0:C0.C127</b>), fill in + * a newly allocated apol_mls_level_t and return it. The category + * portion of the level will <strong>not</strong> be expanded (i.e., + * dots will not be resolved). The caller must call + * apol_mls_level_destroy() upon the returned value afterwards. + * + * Because this function creates a level without the benefit of a + * policy, its category list is "incomplete" and thus most operations + * will fail. All functions other than apol_mls_level_render(), + * apol_mls_level_convert(), and apol_mls_level_is_literal() will + * result in error. Call apol_mls_level_convert() to make a literal + * MLS level complete, so that it can be used in all functions. + * + * @param mls_level_string Pointer to a string representing a + * (possibly invalid) MLS level. + * + * @return A filled in MLS level structure, or NULL upon error. + */ + extern apol_mls_level_t *apol_mls_level_create_from_literal(const char *mls_level_string); + +/** + * Create a new apol_mls_level_t and initialize it with a + * qpol_mls_level_t. The caller must call apol_mls_level_destroy() + * upon the returned value afterwards. + * + * @param p Policy from which the qpol_mls_level_t was obtained. + * @param qpol_level The libqpol level for which to create a new + * apol level. This level will not be altered by this call. + * + * @return A MLS level structure initialized to the value of + * qpol_level, or NULL upon error. + */ + extern apol_mls_level_t *apol_mls_level_create_from_qpol_mls_level(const apol_policy_t * p, + const qpol_mls_level_t * qpol_level); + +/** + * Create a new apol_mls_level_t and initialize it with a + * qpol_level_t. The caller must call apol_mls_level_destroy() + * upon the returned value afterwards. + * + * @param p Policy from which the qpol_level_t was obtained. + * @param qpol_level The libqpol level for which to create a new + * apol level. This level will not be altered by this call. + * + * @return A MLS level structure initialized to the value of + * qpol_level, or NULL upon error. + */ + apol_mls_level_t *apol_mls_level_create_from_qpol_level_datum(const apol_policy_t * p, const qpol_level_t * qpol_level); + +/** + * Deallocate all memory associated with a MLS level structure and + * then set it to NULL. This function does nothing if the level is + * already NULL. + * + * @param level Reference to a MLS level structure to destroy. + */ + extern void apol_mls_level_destroy(apol_mls_level_t ** level); + +/** + * Set the sensitivity component of an MLS level structure. This + * function duplicates the incoming string. + * + * @param p Error reporting handler, or NULL to use default handler. + * @param level MLS level to modify. + * @param sens New sensitivity component to set, or NULL to unset this + * field. + * + * @return 0 on success, negative on error. + */ + extern int apol_mls_level_set_sens(const apol_policy_t * p, apol_mls_level_t * level, const char *sens); + +/** + * Get the sensitivity component of an MLS level structure. + * + * @param level MLS level to query. + * + * @return The sensitivity, or NULL upon error if it has not yet been + * set. Do not modify the return value. + */ + extern const char *apol_mls_level_get_sens(const apol_mls_level_t * level); + +/** + * Add a category component of an MLS level structure. This function + * duplicates the incoming string. + * + * @param p Error reporting handler, or NULL to use default handler. + * @param level MLS level to modify. + * @param cats New category component to append. + * + * @return 0 on success or < 0 on failure. + */ + extern int apol_mls_level_append_cats(const apol_policy_t * p, apol_mls_level_t * level, const char *cats); + +/** + * Get the category component of an MLS level structure. This will be + * a vector of strings, sorted alphabetically. + * + * @param level MLS level to query. + * + * @return Vector of categories, or NULL upon error. Be aware that + * the vector could be empty if no categories have been set. Do not + * modify the return value. + */ + extern const apol_vector_t *apol_mls_level_get_cats(const apol_mls_level_t * level); + +/** + * Compare two levels and determine their relationship to each other. + * Both levels must have their respective sensitivity and categories + * set. Levels may contain aliases in place of primary names. If + * level2 is NULL then this always returns APOL_MLS_EQ. + * + * @param p Policy within which to look up MLS information. + * @param target Target MLS level to compare. + * @param search Source MLS level to compare. + * + * @return One of APOL_MLS_EQ, APOL_MLS_DOM, APOL_MLS_DOMBY, or + * APOL_MLS_INCOMP; < 0 on error. + * + * @see apol_mls_level_validate() + */ + extern int apol_mls_level_compare(const apol_policy_t * p, const apol_mls_level_t * level1, + const apol_mls_level_t * level2); + +/** + * Given a level, determine if it is legal according to the supplied + * policy. This function will convert from aliases to canonical forms + * as necessary. + * + * @param h Error reporting handler. + * @param p Policy within which to look up MLS information. + * @param level Level to check. + * + * @return 1 If level is legal, 0 if not; < 0 on error. + * + * @see apol_mls_level_compare() + */ + extern int apol_mls_level_validate(const apol_policy_t * p, const apol_mls_level_t * level); + +/** + * Creates a string containing the textual representation of + * a MLS level. + * @param p Policy from which the MLS level is a member. If NULL, + * then attempt to treat the level as an incomplete level (as per + * apol_mls_level_create_from_literal()). + * @param level MLS level to render. + * + * @return A newly allocated string, or NULL upon error. The caller + * is responsible for calling free() upon the return value. + */ + extern char *apol_mls_level_render(const apol_policy_t * p, const apol_mls_level_t * level); + +/** + * Given a policy and a MLS level created by + * apol_mls_level_create_from_literal(), convert the level to have a + * valid ("complete") list of categories. This will take the literal + * string stored within the level and resolve its category lists, such + * as by expanding dots. The level will keep its literal string, so + * that it may be converted again if given a different policy. + * + * @param p Policy containing category information. + * @param level MLS level to convert. + * + * @return 0 on success, < 0 on error. + */ + extern int apol_mls_level_convert(const apol_policy_t * p, apol_mls_level_t * level); + +/** + * Determine if a level is literal (i.e., created from + * apol_mls_level_create_from_literal()). Note that converting a + * literal level (apol_mls_level_convert()) completes the level, but + * it is still a literal level. + * + * @param level Level to query. + * + * @return > 0 value if the level is literal, 0 if not, < 0 if unknown + * or upon error. + */ + extern int apol_mls_level_is_literal(const apol_mls_level_t * level); + +#ifdef __cplusplus +} +#endif + +#endif /* APOL_MLS_LEVEL_H */ diff --git a/libapol/include/apol/mls_range.h b/libapol/include/apol/mls_range.h new file mode 100644 index 0000000..49dade7 --- /dev/null +++ b/libapol/include/apol/mls_range.h @@ -0,0 +1,270 @@ +/** + * @file + * Public interface for representing and manipulating the + * apol_mls_range object. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_MLS_RANGE_H +#define APOL_MLS_RANGE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "mls_level.h" +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + + typedef struct apol_mls_range apol_mls_range_t; + +/** + * Allocate and return a new MLS range structure. All fields are + * initialized to nothing. The caller must call + * apol_mls_range_destroy() upon the return value afterwards. + * + * @return An initialized MLS range structure, or NULL upon error. + */ + extern apol_mls_range_t *apol_mls_range_create(void); + +/** + * Allocate and return a new MLS range structure, initialized by an + * existing apol_mls_range_t. The caller must call + * apol_mls_range_destroy() upon the return value afterwards. + * + * @param range Range to copy. If NULL then the returned MLS range + * will be initialized to nothing. + * + * @return An initialized MLS range structure, or NULL upon error. + */ + extern apol_mls_range_t *apol_mls_range_create_from_mls_range(const apol_mls_range_t * range); + +/** + * Take a MLS range string (e.g., <b>S0:C0.C10-S1:C0.C127</b>) and + * parse it. Fill in a newly allocated apol_mls_range_t and return + * it. This function needs a policy to resolve dots within categories + * and to ensure that the high level dominates the low. If the string + * represents an illegal range then return NULL. The caller must call + * apol_mls_range_destroy() upon the returned value afterwards. + * + * @param p Policy within which to validate mls_range_string. + * @param mls_range_string Pointer to a string representing a valid + * MLS range. + * + * @return A filled in MLS range structure, or NULL upon error. + */ + extern apol_mls_range_t *apol_mls_range_create_from_string(const apol_policy_t * p, const char *mls_range_string); + +/** + * Take a literal MLS range string (e.g., + * <b>S0:C0.C10-S1:C0.C127</b>), fill in a newly allocated + * apol_mls_range_t and return it. The category portions of the + * levels will <strong>not</strong> be expanded (i.e., dots will not + * be resolved); likewise there is no check that the high level + * dominates the low. The caller must call apol_mls_range_destroy() + * upon the returned value afterwards. + * + * Because this function creates a range without the benefit of a + * policy, its levels are "incomplete" and thus most operations will + * fail. Call apol_mls_range_convert() to make a literal MLS range + * complete, so that it can be used in all functions. + * + * @param mls_range_string Pointer to a string representing a + * (possibly invalid) MLS range. + * + * @return A filled in MLS range structure, or NULL upon error. + */ + extern apol_mls_range_t *apol_mls_range_create_from_literal(const char *mls_range_string); + +/** + * Create a new apol_mls_range_t and initialize it with a + * qpol_mls_range_t. The caller must call apol_mls_range_destroy() + * upon the return value afterwards. + * + * @param p Policy from which the qpol_mls_range_t was obtained. + * @param qpol_range The libqpol range for which to create a new + * apol range. This range will not be altered by this call. + * + * @return A MLS range structure initialized to the value of + * qpol_range, or NULL upon error. + */ + extern apol_mls_range_t *apol_mls_range_create_from_qpol_mls_range(const apol_policy_t * p, + const qpol_mls_range_t * qpol_range); + +/** + * Deallocate all memory associated with a MLS range structure and + * then set it to NULL. This function does nothing if the range is + * already NULL. + * + * @param range Reference to a MLS range structure to destroy. + */ + extern void apol_mls_range_destroy(apol_mls_range_t ** range); + +/** + * Set the low level component of a MLS range structure. This + * function takes ownership of the level, such that the caller must + * not modify nor destroy it afterwards. It is legal to pass in the + * same pointer for the range's low and high level. + * + * @param p Error reporting handler, or NULL to use default handler. + * @param range MLS range to modify. + * @param level New low level for range, or NULL to unset this field. + * + * @return 0 on success or < 0 on failure. + */ + extern int apol_mls_range_set_low(const apol_policy_t * p, apol_mls_range_t * range, apol_mls_level_t * level); + +/** + * Set the high level component of a MLS range structure. This + * function takes ownership of the level, such that the caller must + * not modify nor destroy it afterwards. It is legal to pass in the + * same pointer for the range's low and high level. + * + * @param p Error reporting handler, or NULL to use default handler. + * @param range MLS range to modify. + * @param level New high level for range, or NULL to unset this field. + * + * @return 0 on success or < 0 on failure. + */ + extern int apol_mls_range_set_high(const apol_policy_t * p, apol_mls_range_t * range, apol_mls_level_t * level); + +/** + * Get the low level component of a MLS range structure. + * + * @param range MLS range to query. + * + * @return Low level, or NULL upon error or if not yet set. Do not + * modify the return value. + */ + extern const apol_mls_level_t *apol_mls_range_get_low(const apol_mls_range_t * range); + +/** + * Get the high level component of a MLS range structure. + * + * @param range MLS range to query. + * + * @return High level, or NULL upon error or if not yet set. Do not + * modify the return value. + */ + extern const apol_mls_level_t *apol_mls_range_get_high(const apol_mls_range_t * range); + +/** + * Compare two ranges, determining if one matches the other. The + * fifth parameter gives how to match the ranges. For APOL_QUERY_SUB, + * if search is a subset of target. For APOL_QUERY_SUPER, if search + * is a superset of target. Other valid compare types are + * APOL_QUERY_EXACT and APOL_QUERY_INTERSECT. If a range is not valid + * according to the policy then this function returns -1. If search + * is NULL then comparison always succeeds. + * + * @param p Policy within which to look up MLS information. + * @param target Target MLS range to compare. + * @param search Source MLS range to compare. + * @param range_compare_type Specifies how to compare the ranges. + * + * @return 1 If comparison succeeds, 0 if not; -1 on error. + */ + extern int apol_mls_range_compare(const apol_policy_t * p, + const apol_mls_range_t * target, const apol_mls_range_t * search, + unsigned int range_compare_type); + +/** + * Determine if a range completely contains a subrange given a certain + * policy. If a range is not valid according to the policy then this + * function returns -1. + * + * @param p Policy within which to look up MLS information. + * @param range Parent range to compare. + * @param subrange Child range to which compare. + * + * @return 1 If comparison succeeds, 0 if not; -1 on error. + */ + extern int apol_mls_range_contain_subrange(const apol_policy_t * p, const apol_mls_range_t * range, + const apol_mls_range_t * subrange); +/** + * Given a range, determine if it is legal according to the supplied + * policy. This function will convert from aliases to canonical forms + * as necessary. + * + * @param p Policy within which to look up MLS information. + * @param range Range to check. + * + * @return 1 If range is legal, 0 if not; -1 on error. + */ + extern int apol_mls_range_validate(const apol_policy_t * p, const apol_mls_range_t * range); + +/** + * Given a range, return a vector of levels (type apol_mls_level_t *) + * that constitutes that range. The vector will be sorted in policy order. + * + * @param p Policy from which the level and category definitions reside. + * @param range Range to expand. + * + * @return Vector of levels, or NULL upon error. The caller is + * responsible for calling apol_vector_destroy() upon the returned + * value, passing apol_mls_level_free() as the second parameter. + */ + extern apol_vector_t *apol_mls_range_get_levels(const apol_policy_t * p, const apol_mls_range_t * range); + +/** + * Creates a string containing the textual representation of + * a MLS range. + * + * @param p Policy from which the MLS range is a member. If NULL, + * then attempt to treat the range's levels as incomplete levels (as + * per apol_mls_level_create_from_literal()). + * @param range MLS range to render. + * + * @return A newly allocated string, or NULL upon error. The caller + * is responsible for calling free() upon the return value. + */ + extern char *apol_mls_range_render(const apol_policy_t * p, const apol_mls_range_t * range); + +/** + * Given a range, convert any literal MLS levels within it (as per + * apol_mls_level_convert()) to a complete level. If the range has no + * levels or has no literal levels then do nothing. + * + * @param p Policy containing category information. + * @param range Range to convert. + * + * @return 0 on success, < 0 on error. + */ + extern int apol_mls_range_convert(const apol_policy_t * p, apol_mls_range_t * range); + +/** + * Determine if the range contains any literal levels. (Levels that + * have been converted are still considered literal.) + * + * @param range Range to query. + * + * @return > 0 value if the range has a literal level, 0 if not, < 0 + * if unknown or upon error. + */ + extern int apol_mls_range_is_literal(const apol_mls_range_t * range); + +#ifdef __cplusplus +} +#endif + +#endif /* APOL_MLS_RANGE_H */ diff --git a/libapol/include/apol/netcon-query.h b/libapol/include/apol/netcon-query.h new file mode 100644 index 0000000..b74b229 --- /dev/null +++ b/libapol/include/apol/netcon-query.h @@ -0,0 +1,364 @@ +/** + * @file + * Public Interface for querying portcons, netifcons, and nodecons of + * a policy. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_NETCON_QUERY_H +#define APOL_NETCON_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include "context-query.h" +#include <qpol/policy.h> + + typedef struct apol_portcon_query apol_portcon_query_t; + typedef struct apol_netifcon_query apol_netifcon_query_t; + typedef struct apol_nodecon_query apol_nodecon_query_t; + +/******************** portcon queries ********************/ + +/** + * Execute a query against all portcons within the policy. The + * returned portcons will be unordered. + * + * @param p Policy within which to look up portcons. + * @param po Structure containing parameters for query. If this is + * NULL then return all portcons. + * @param v Reference to a vector of qpol_portcon_t. The vector will + * be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_portcon_get_by_query(const apol_policy_t * p, const apol_portcon_query_t * po, apol_vector_t ** v); + +/** + * Allocate and return a new portcon query structure. All fields are + * initialized, such that running this blank query results in + * returning all portcons within the policy. The caller must call + * apol_portcon_query_destroy() upon the return value afterwards. + * + * @return An initialized portcon query structure, or NULL upon error. + */ + extern apol_portcon_query_t *apol_portcon_query_create(void); + +/** + * Deallocate all memory associated with the referenced portcon + * query, and then set it to NULL. This function does nothing if the + * query is already NULL. + * + * @param po Reference to a portcon query structure to destroy. + */ + extern void apol_portcon_query_destroy(apol_portcon_query_t ** po); + +/** + * Set a portcon query to return only portcons that use this protocol. + * + * @param p Policy handler, to report errors. + * @param po Portcon query to set. + * @param proto Limit query to only portcons with this protocol, or + * negative to unset this field. + * + * @return Always 0. + */ + extern int apol_portcon_query_set_protocol(const apol_policy_t * p, apol_portcon_query_t * po, int proto); + +/** + * Set a portcon query to return only portcons with this as their low + * port. + * + * @param p Policy handler, to report errors. + * @param po Portcon query to set. + * @param low Limit query to only portcons with this low port, or + * negative to unset this field. + * + * @return Always 0. + */ + extern int apol_portcon_query_set_low(const apol_policy_t * p, apol_portcon_query_t * po, int low); + +/** + * Set a portcon query to return only portcons with this as their high + * port. + * + * @param p Policy handler, to report errors. + * @param po Portcon query to set. + * @param high Limit query to only portcons with this high port, or + * negative to unset this field. + * + * @return Always 0. + */ + extern int apol_portcon_query_set_high(const apol_policy_t * p, apol_portcon_query_t * po, int high); + +/** + * Set a portcon query to return only portcons matching a context. + * This function takes ownership of the context, such that the caller + * must not modify nor destroy it afterwards. + * + * @param p Policy handler, to report errors. + * @param po Portcon query to set. + * @param context Limit query to only portcons matching this context, + * or NULL to unset this field. + * @param range_match Specifies how to match the MLS range within the + * context. This must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or + * APOL_QUERY_EXACT. This parameter is ignored if context is NULL. + * + * @return Always returns 0. + */ + extern int apol_portcon_query_set_context(const apol_policy_t * p, + apol_portcon_query_t * po, apol_context_t * context, unsigned int range_match); + +/** + * Creates a string containing the textual representation of + * a portcon type. + * @param p Reference to a policy. + * @param portcon Reference to the portcon statement to be rendered. + * + * @return A newly allocated string on success, caller must free; + * NULL on error. + */ + extern char *apol_portcon_render(const apol_policy_t * p, const qpol_portcon_t * portcon); + +/******************** netifcon queries ********************/ + +/** + * Execute a query against all netifcons within the policy. The + * returned netifcons will be unordered. + * + * @param p Policy within which to look up netifcons. + * @param n Structure containing parameters for query. If this is + * NULL then return all netifcons. + * @param v Reference to a vector of qpol_netifcon_t. The vector will + * be allocated by this function. The caller must call + * apol_vector_destroy() afterwards,. This will be set to NULL upon + * no results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_netifcon_get_by_query(const apol_policy_t * p, const apol_netifcon_query_t * n, apol_vector_t ** v); + +/** + * Allocate and return a new netifcon query structure. All fields are + * initialized, such that running this blank query results in + * returning all netifcons within the policy. The caller must call + * apol_netifcon_query_destroy() upon the return value afterwards. + * + * @return An initialized netifcon query structure, or NULL upon + * error. + */ + extern apol_netifcon_query_t *apol_netifcon_query_create(void); + +/** + * Deallocate all memory associated with the referenced netifcon + * query, and then set it to NULL. This function does nothing if the + * query is already NULL. + * + * @param n Reference to a netifcon query structure to destroy. + */ + extern void apol_netifcon_query_destroy(apol_netifcon_query_t ** n); + +/** + * Set a netifcon query to return only netifcons that use this device. + * + * @param p Policy handler, to report errors. + * @param n Netifcon query to set. + * @param dev Limit query to only netifcons that use this device, or + * NULL to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_netifcon_query_set_device(const apol_policy_t * p, apol_netifcon_query_t * n, const char *dev); + +/** + * Set a netifcon query to return only netifcons matching this context + * for its interface. This function takes ownership of the context, + * such that the caller must not modify nor destroy it afterwards. + * + * @param p Policy handler, to report errors. + * @param n Netifcon query to set. + * @param context Limit query to only netifcon matching this context + * for its interface, or NULL to unset this field. + * @param range_match Specifies how to match the MLS range within the + * context. This must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or + * APOL_QUERY_EXACT. This parameter is ignored if context is NULL. + * + * @return Always returns 0. + */ + extern int apol_netifcon_query_set_if_context(const apol_policy_t * p, + apol_netifcon_query_t * n, apol_context_t * context, + unsigned int range_match); + +/** + * Set a netifcon query to return only netifcons matching this context + * for its messages. This function takes ownership of the context, + * such that the caller must not modify nor destroy it afterwards. + * + * @param p Policy handler, to report errors. + * @param n Netifcon query to set. + * @param context Limit query to only netifcon matching this context + * for its messages, or NULL to unset this field. + * @param range_match Specifies how to match the MLS range within the + * context. This must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or + * APOL_QUERY_EXACT. This parameter is ignored if context is NULL. + * + * @return Always returns 0. + */ + extern int apol_netifcon_query_set_msg_context(const apol_policy_t * p, + apol_netifcon_query_t * n, apol_context_t * context, + unsigned int range_match); + +/** + * Creates a string containing the textual representation of + * a netifcon type. + * @param p Reference to a policy. + * @param netifcon Reference to the netifcon statement to be rendered. + * + * @return A newly allocated string on success, caller must free; + * NULL on error. + */ + extern char *apol_netifcon_render(const apol_policy_t * p, const qpol_netifcon_t * netifcon); + +/******************** nodecon queries ********************/ + +/** + * Execute a query against all nodecons within the policy. The + * returned nodecons will be unordered. + * + * @param p Policy within which to look up nodecons. + * @param n Structure containing parameters for query. If this is + * NULL then return all nodecons. + * @param v Reference to a vector of qpol_nodecon_t. The vector will + * be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_nodecon_get_by_query(const apol_policy_t * p, const apol_nodecon_query_t * n, apol_vector_t ** v); + +/** + * Allocate and return a new nodecon query structure. All fields are + * initialized, such that running this blank query results in + * returning all nodecons within the policy. The caller must call + * apol_nodecon_query_destroy() upon the return value afterwards. + * + * @return An initialized nodecon query structure, or NULL upon + * error. + */ + extern apol_nodecon_query_t *apol_nodecon_query_create(void); + +/** + * Deallocate all memory associated with the referenced nodecon + * query, and then set it to NULL. This function does nothing if the + * query is already NULL. + * + * @param n Reference to a nodecon query structure to destroy. + */ + extern void apol_nodecon_query_destroy(apol_nodecon_query_t ** n); + +/** + * Set a nodecon query to return only nodecons with this protocol, + * either IPv4 or IPv6. + * + * @param p Policy handler, to report errors. + * @param n Nodecon query to set. + * @param proto Limit query to only this protocol, either QPOL_IPV4 or + * QPOL_IPV6, or a negative value to unset this field. + * + * @return 0 if protocol was valid, -1 on error. + */ + extern int apol_nodecon_query_set_protocol(const apol_policy_t * p, apol_nodecon_query_t * n, int proto); + +/** + * Set a nodecon query to return only nodecons with this address. If + * the protocol is QPOL_IPV4 then only the first element of the + * address array is used, for QPOL_IPV6 all four are used. + * + * @param p Policy handler, to report errors. + * @param n Nodecon query to set. + * @param addr Array of no more than four elements representing the + * address, or NULL to unset this field. This function will make a + * copy of the array. + * @param proto Format of address, either QPOL_IPV4 or QPOL_IPV6. + * This parameter is ignored if addr is NULL. + * + * @return 0 if protocol was valid, -1 on error. + */ + extern int apol_nodecon_query_set_addr(const apol_policy_t * p, apol_nodecon_query_t * n, uint32_t * addr, int proto); + +/** + * Set a nodecon query to return only nodecons with this netmask. If + * the protocol is QPOL_IPV4 then only the first element of the mask + * array is used, for QPOL_IPV6 all four are used. + * + * @param p Policy handler, to report errors. + * @param n Nodecon query to set. + * @param mask Array of no more than four elements representing the + * netmask, or NULL to unset this field. This function will make a + * copy of the array. + * @param proto Format of mask, either QPOL_IPV4 or QPOL_IPV6. This + * parameter is ignored if mask is NULL. + * + * @return 0 if protocol was valid, -1 on error. + */ + extern int apol_nodecon_query_set_mask(const apol_policy_t * p, apol_nodecon_query_t * n, uint32_t * mask, int proto); + +/** + * Set a nodecon query to return only nodecons matching this context. + * This function takes ownership of the context, such that the caller + * must not modify nor destroy it afterwards. + * + * @param p Policy handler, to report errors. + * @param n Nodecon query to set. + * @param context Limit query to only nodecons matching this context, + * or NULL to unset this field. + * @param range_match Specifies how to match the MLS range within the + * context. This must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or + * APOL_QUERY_EXACT. This parameter is ignored if context is NULL. + * + * @return Always returns 0. + */ + extern int apol_nodecon_query_set_context(const apol_policy_t * p, + apol_nodecon_query_t * n, apol_context_t * context, unsigned int range_match); + +/** + * Creates a string containing the textual representation of + * a nodecon type. + * @param p Reference to a policy. + * @param nodecon Reference to the nodecon statement to be rendered. + * + * @return A newly allocated string on success, caller must free; + * NULL on error. + */ + extern char *apol_nodecon_render(const apol_policy_t * p, const qpol_nodecon_t * nodecon); + +#ifdef __cplusplus +} +#endif + +#endif /* APOL_NETCON_QUERY_H */ diff --git a/libapol/include/apol/perm-map.h b/libapol/include/apol/perm-map.h new file mode 100644 index 0000000..ea2c0e7 --- /dev/null +++ b/libapol/include/apol/perm-map.h @@ -0,0 +1,137 @@ +/** + * @file + * + * Permission mapping routines for libapol. These maps assoicate all + * object class permissions with read, write, read&write, and none + * access. These maps are used, for example, by an information flow + * analysis. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_PERMMAP_H +#define APOL_PERMMAP_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" + +#define APOL_PERMMAP_MAX_WEIGHT 10 +#define APOL_PERMMAP_MIN_WEIGHT 1 + +#define APOL_PERMMAP_UNMAPPED 0x00 /* defined object/perm, but no map */ +#define APOL_PERMMAP_READ 0x01 +#define APOL_PERMMAP_WRITE 0x02 +#define APOL_PERMMAP_BOTH (APOL_PERMMAP_READ | APOL_PERMMAP_WRITE) +#define APOL_PERMMAP_NONE 0x10 + +/** + * Read a permission map from a file into a policy. If there is a + * non-fatal error while loading (e.g., file declared an object class + * that does not exist within the policy) then generate a warning + * string and send it to the error handler stored within the policy. + * + * If a permission map was already loaded, then the existing one will + * be destroyed. + * + * @param p Policy to which store permission map. + * @param filename Name of file containing permission map. + * + * @return 0 on success, > 0 on success with warnings, < 0 on error. + */ + extern int apol_policy_open_permmap(apol_policy_t * p, const char *filename); + +/** + * @deprecated Use apol_policy_open_permmap(). + */ + extern int apol_permmap_load(apol_policy_t * p, const char *filename) __attribute__ ((deprecated)); + +/** + * Write the contents of permission map to a file. Any existing file + * will be overwritten. + * + * @param p Policy containing permission map. + * @param filename Destination filename. + * + * @return 0 on success, < 0 on error. + */ + extern int apol_policy_save_permmap(const apol_policy_t * p, const char *filename); + +/** + * @deprecated Use apol_policy_save_permmap(). + */ + extern int apol_permmap_save(apol_policy_t * p, const char *filename) __attribute__ ((deprecated)); + +/** + * Given a class and permission name, look up that permission mapping + * within a policy's permission map. Set the reference variables map + * and weight to the mapping. + * + * @param p Policy containing permission map. + * @param class_name Name of class to find. + * @param perm_name Permission within class to find. + * @param map Location to store mapping, one of APOL_PERMMAP_UNMAPPED, + * etc. + * @param weight Weight of this permission, a value between + * APOL_PERMMAP_MIN_WEIGHT and APOL_PERMMAP_MAX_WEIGHT, inclusive. + * + * @return 0 if class and permission were found, < 0 on error or if + * not found. + */ + extern int apol_policy_get_permmap(const apol_policy_t * p, const char *class_name, const char *perm_name, int *map, + int *weight); + +/** + * @deprecated Use apol_policy_get_permmap(). + */ + extern int apol_permmap_get(apol_policy_t * p, const char *class_name, const char *perm_name, int *map, int *weight) + __attribute__ ((deprecated)); + +/** + * Given a class and permission name, set that permission's map and + * weight within the policy's permission map. + * + * @param p Policy containing permission map. + * @param class_name Name of class to find. + * @param perm_name Permission within class to find. + * @param map New map value, one of APOL_PERMMAP_UNMAPPED, etc. + * @param weight New weight of this permission. If the value will be + * clamped to be between APOL_PERMMAP_MIN_WEIGHT and + * APOL_PERMMAP_MAX_WEIGHT, inclusive. + * + * @return 0 if permission map was changed, < 0 on error or if not + * found. + */ + extern int apol_policy_set_permmap(apol_policy_t * p, const char *class_name, const char *perm_name, int map, int weight); + +/** + * @deprecated Use apol_policy_set_permmap(). + */ + extern int apol_permmap_set(apol_policy_t * p, const char *class_name, const char *perm_name, int map, int weight) + __attribute__ ((deprecated)); + +#ifdef __cplusplus +} +#endif + +#endif /*APOL_PERMMAP_H */ diff --git a/libapol/include/apol/permissive-query.h b/libapol/include/apol/permissive-query.h new file mode 100644 index 0000000..988ea47 --- /dev/null +++ b/libapol/include/apol/permissive-query.h @@ -0,0 +1,104 @@ +/** + * @file + * + * Routines to query permissive types in policy. + * + * @author Steve Lawrence slawrence@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_PERMISSIVE_QUERY_H +#define APOL_PERMISSIVE_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + + typedef struct apol_permissive_query apol_permissive_query_t; + +/** + * Execute a query against all permissive types within the policy. The results + * will only contain permissive types, not aliases nor attributes. + * + * @param p Policy within which to look up permissive types. + * @param t Structure containing parameters for query. If this is + * NULL then return all permissive types. + * @param v Reference to a vector of qpol_permissive_t. The vector will be + * allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_permissive_get_by_query(const apol_policy_t * p, apol_permissive_query_t * t, apol_vector_t ** v); + +/** + * Allocate and return a new permissive query structure. All fields are + * initialized, such that running this blank query results in + * returning all permissive types within the policy. The caller must call + * apol_permissive_query_destroy() upon the return value afterwards. + * + * @return An initialized permissive query structure, or NULL upon error. + */ + extern apol_permissive_query_t *apol_permissive_query_create(void); + +/** + * Deallocate all memory associated with the referenced permissive query, + * and then set it to NULL. This function does nothing if the query + * is already NULL. + * + * @param t Reference to a permissive query structure to destroy. + */ + extern void apol_permissive_query_destroy(apol_permissive_query_t ** t); + +/** + * Set a permissive query to return only permissive types that match this name. This function + * duplicates the incoming name. + * + * @param p Policy handler, to report errors. + * @param t Permissive query to set. + * @param name Limit query to only permissive types with this name, or + * NULL to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_permissive_query_set_name(const apol_policy_t * p, apol_permissive_query_t * t, const char *name); + +/** + * Set a permissive query to use regular expression searching for all of its + * fields. Strings will be treated as regexes instead of literals. + * Matching will occur against the permissive type name. + * + * @param p Policy handler, to report errors. + * @param t Permissive query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_permissive_query_set_regex(const apol_policy_t * p, apol_permissive_query_t * t, int is_regex); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/polcap-query.h b/libapol/include/apol/polcap-query.h new file mode 100644 index 0000000..b8f7d6b --- /dev/null +++ b/libapol/include/apol/polcap-query.h @@ -0,0 +1,103 @@ +/** + * @file + * + * Routines to query policy capabilities in policy. + * + * @author Steve Lawrence slawrence@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_POLCAP_QUERY_H +#define APOL_POLCAP_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + + typedef struct apol_polcap_query apol_polcap_query_t; + +/** + * Execute a query against all policy capabilities within the policy. + * + * @param p Policy within which to look up policy capabilities. + * @param t Structure containing parameters for query. If this is + * NULL then return all policy capabilities. + * @param v Reference to a vector of qpol_polcap_t. The vector will be + * allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_polcap_get_by_query(const apol_policy_t * p, apol_polcap_query_t * t, apol_vector_t ** v); + +/** + * Allocate and return a new polcap query structure. All fields are + * initialized, such that running this blank query results in + * returning all policy capabilities within the policy. The caller must call + * apol_polcap_query_destroy() upon the return value afterwards. + * + * @return An initialized polcap query structure, or NULL upon error. + */ + extern apol_polcap_query_t *apol_polcap_query_create(void); + +/** + * Deallocate all memory associated with the referenced polcap query, + * and then set it to NULL. This function does nothing if the query + * is already NULL. + * + * @param t Reference to a polcap query structure to destroy. + */ + extern void apol_polcap_query_destroy(apol_polcap_query_t ** t); + +/** + * Set a polcap query to return only policy capabilities that match this name. This function + * duplicates the incoming name. + * + * @param p Policy handler, to report errors. + * @param t Polcap query to set. + * @param name Limit query to only policy capabilities with this name, or + * NULL to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_polcap_query_set_name(const apol_policy_t * p, apol_polcap_query_t * t, const char *name); + +/** + * Set a polcap query to use regular expression searching for all of its + * fields. Strings will be treated as regexes instead of literals. + * Matching will occur against the policy capability name. + * + * @param p Policy handler, to report errors. + * @param t Polcap query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_polcap_query_set_regex(const apol_policy_t * p, apol_polcap_query_t * t, int is_regex); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/policy-path.h b/libapol/include/apol/policy-path.h new file mode 100644 index 0000000..771fdf5 --- /dev/null +++ b/libapol/include/apol/policy-path.h @@ -0,0 +1,194 @@ +/** + * @file + * + * An opaque structure that represents a policy "path". A policy path + * may really be a base policy and a number of modules, thus a single + * string is not sufficient. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_POLICY_PATH_H +#define APOL_POLICY_PATH_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "vector.h" + + typedef struct apol_policy_path apol_policy_path_t; + +/** + * Type of policy this path represents - either a single path, for a + * monolithic policy, or a path + multiple modules for modular policy. + */ + typedef enum apol_policy_path_type + { + APOL_POLICY_PATH_TYPE_MONOLITHIC = 0, + APOL_POLICY_PATH_TYPE_MODULAR + } apol_policy_path_type_e; + +/** + * Create a policy path from scratch. The resulting object represents + * the file or files needed to load a policy. + * + * @param path_type Type of policy to represent. + * @param path Primary path name. For modular policies this is the + * base policy's path. + * @param modules Vector of strings representing modules' paths. The + * vector can be NULL to mean no modules. This parameter is ignored + * if path_type is not APOL_POLICY_PATH_TYPE_MODULAR. The function + * will duplicate the vector and its contents. + * + * @return An apol_policy_path object, or NULL upon error. + */ + extern apol_policy_path_t *apol_policy_path_create(apol_policy_path_type_e path_type, const char *path, + const apol_vector_t * modules); + +/** + * Create a policy path, initialized from another policy path. This + * function recursively duplicates all data within the original path. + * + * @param path Policy path to duplicate. + * + * @return An apol_policy_path object, or NULL upon error. + */ + extern apol_policy_path_t *apol_policy_path_create_from_policy_path(const apol_policy_path_t * path); + +/** + * Create a policy path, initialize by the contents of a <em>policy + * path list</em> file. Call apol_policy_path_to_filename() to write + * a policy path list to disk. + * + * @param filename Name of the file containing a policy path list. + * + * @return An apol_policy_path object, or NULL upon error. + */ + extern apol_policy_path_t *apol_policy_path_create_from_file(const char *filename); + +/** + * Create a policy path, initialized by a special path format string. + * Call apol_policy_path_to_string() to create this string. + * + * @param path_string String containing initialization data for the + * object. + * + * @return An apol_policy_path object, or NULL upon error. + */ + extern apol_policy_path_t *apol_policy_path_create_from_string(const char *path_string); + +/** + * Destroy the referencened policy path object. + * + * @param path Policy path to destroy. The pointer will be set to + * NULL afterwards. (If pointer is already NULL then do nothing.) + */ + extern void apol_policy_path_destroy(apol_policy_path_t ** path); + +/** + * Compare two policy paths, determining if one is different than the + * other. The returned value is stable, in that it may be used as the + * basis for sorting a list of policy paths. Monolithic policies are + * considered "less than" modular policies. + * + * @param a First policy path to compare. + * @param b Second policy path to compare. + * + * @return < 0 if path A is "less than" B, > 0 if A is "greater than" + * B, or 0 if equivalent or undeterminable. + */ + extern int apol_policy_path_compare(const apol_policy_path_t * a, const apol_policy_path_t * b); + +/** + * Get the type of policy this path object represents. + * + * @param path Policy path object to query. + * + * @return Type of policy the object represents. + */ + extern apol_policy_path_type_e apol_policy_path_get_type(const apol_policy_path_t * path); + +/** + * Get the primary path name from a path object. For monolithic + * policies this is the path to the policy. For modular policies this + * is the base policy path. + * + * @param path Policy path object to query. + * + * @return Primary path, or NULL upon error. Do not modify + * this string. + */ + extern const char *apol_policy_path_get_primary(const apol_policy_path_t * path); + +/** + * Get the list of modules from a path object. This will be a vector + * of strings. It is an error to call this function for non-modular + * policies. + * + * @param path Policy path object to query. + * + * @return Vector of module paths, or NULL upon error. Do not modify + * this vector or its contents. Note that the vector could be empty. + */ + extern const apol_vector_t *apol_policy_path_get_modules(const apol_policy_path_t * path); + +/** + * Write a human-readable <em>policy path list</em> to disk. This + * file describes a policy path and is suitable as input to + * apol_policy_path_create_from_file(). + * + * @param path Policy path to write to disk. + * @param filename Name of the file to write policy path list. If the + * file already exists it will be overwritten. + * + * @return 0 on successful write, < 0 on error. + */ + extern int apol_policy_path_to_file(const apol_policy_path_t * path, const char *filename); + +/** + * Encode a path object into a specially formatted string. The + * resulting string is suitable as input to + * apol_policy_path_create_from_string(). + * + * @param path Policy path object to encode. + * + * @return Formatted string for the path object, or NULL upon error. + * The caller is responsible for calling free() upon the returned + * value. + */ + extern char *apol_policy_path_to_string(const apol_policy_path_t * path); + +/** + * Determine if a file is a policy path list. + * + * @param filename Name of the file to test. + * + * @return > 0 if the file is a policy path list, 0 if it is not, + * and < 0 on error. + */ + extern int apol_file_is_policy_path_list(const char *filename); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/policy-query.h b/libapol/include/apol/policy-query.h new file mode 100644 index 0000000..315f70e --- /dev/null +++ b/libapol/include/apol/policy-query.h @@ -0,0 +1,86 @@ +/** + * @file + * + * Routines to query parts of a policy. For each component and rule + * there is a query structure to specify the details of the query. + * Analyses are also included by this header file. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_POLICY_QUERY_H +#define APOL_POLICY_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Many libapol queries act upon MLS contexts. Use these defines to + * specify set operations upon contexts. + */ +#define APOL_QUERY_SUB 0x02 /**< The range specified by the query is a subset of the target range */ +#define APOL_QUERY_SUPER 0x04 /**< The range specified by the query is a superset of the target range */ +#define APOL_QUERY_EXACT (APOL_QUERY_SUB|APOL_QUERY_SUPER) /**< The range specified by the query matches the target range exactly. */ +#define APOL_QUERY_INTERSECT 0x08 /* query overlaps any part of rule range */ +#define APOL_QUERY_FLAGS \ + (APOL_QUERY_SUB | APOL_QUERY_SUPER | APOL_QUERY_EXACT | \ + APOL_QUERY_INTERSECT) + +/* The AV rule search and TE rule search use these flags when + * specifying what kind of symbol is being searched. Strings are + * normally interpreted either as a type or as an attribute; the behavior + * can be changed to use only types or only attributes. + */ +#define APOL_QUERY_SYMBOL_IS_TYPE 0x01 +#define APOL_QUERY_SYMBOL_IS_ATTRIBUTE 0x02 + +#include <qpol/policy.h> + +#include "type-query.h" +#include "class-perm-query.h" +#include "role-query.h" +#include "user-query.h" +#include "bool-query.h" +#include "isid-query.h" +#include "mls-query.h" +#include "netcon-query.h" +#include "fscon-query.h" +#include "context-query.h" +#include "permissive-query.h" +#include "polcap-query.h" + +#include "avrule-query.h" +#include "terule-query.h" +#include "condrule-query.h" +#include "rbacrule-query.h" +#include "range_trans-query.h" +#include "constraint-query.h" + +#include "domain-trans-analysis.h" +#include "infoflow-analysis.h" +#include "relabel-analysis.h" +#include "types-relation-analysis.h" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/policy.h b/libapol/include/apol/policy.h new file mode 100644 index 0000000..7b26af8 --- /dev/null +++ b/libapol/include/apol/policy.h @@ -0,0 +1,166 @@ +/** + * @file + * + * Public interface for SELinux policies. This function declares + * apol_policy, a structure that groups a qpol_policy with other + * structures needed by libapol. Almost all setools files will need + * to #include this header. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_POLICY_H +#define APOL_POLICY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy-path.h" +#include <stdarg.h> +#include <qpol/policy.h> + + typedef struct apol_policy apol_policy_t; + + typedef void (*apol_callback_fn_t) (void *varg, const apol_policy_t * p, int level, const char *fmt, va_list argp); + +/** + * When creating an apol_policy, load all components except rules + * (both AV and TE rules). For modular policies, this affects both + * the base policy and subsequent modules. + * @deprecated use QPOL_POLICY_OPTION_NO_RULES instead + */ +#define APOL_POLICY_OPTION_NO_RULES QPOL_POLICY_OPTION_NO_RULES + +/** + * Create a new apol_policy initialized from one or more policy files. + * + * @param path Policy path object specifying which policy file or + * files to load. + * @param options Bitfield specifying options for the returned policy. + * Valid options are QPOL_POLICY_OPTION_* from <qpol/policy.h>. + * @param msg_callback Callback to invoke as errors/warnings are + * generated. If NULL, then write messages to standard error. + * @param varg Value to be passed as the first parameter to the + * callback function. + * + * @return A newly allocated policy that may be used for analysis, or + * NULL upon error. The caller is responsible for calling + * apol_policy_destroy() upon the returned value afterwards. + */ + extern apol_policy_t *apol_policy_create_from_policy_path(const apol_policy_path_t * path, const int options, + apol_callback_fn_t msg_callback, void *varg); + +/** + * Deallocate all memory associated with a policy, including all + * auxillary data structures, and then set it to NULL. Does nothing + * if the pointer is already NULL. + * + * @param policy Policy to destroy, if not already NULL. + */ + extern void apol_policy_destroy(apol_policy_t ** policy); + +/** + * Given a policy, return the policy type. This will be one of + * QPOL_POLICY_KERNEL_SOURCE, QPOL_POLICY_KERNEL_BINARY, or + * QPOL_POLICY_MODULE_BINARY. (You will need to #include + * <qpol/policy.h> to get these definitions.) + * + * @param policy Policy to which check. + * + * @return The policy type, or < 0 upon error. + */ + extern int apol_policy_get_policy_type(const apol_policy_t * policy); + +/** + * Given a policy, return a pointer to the underlying qpol_policy. + * This is needed, for example, to access details of particulary qpol + * components. + * + * @param policy Policy containing qpol policy. + * + * @return Pointer to underlying qpol policy, or NULL on error. Do + * not free() or otherwise destroy this pointer. + */ + extern qpol_policy_t *apol_policy_get_qpol(const apol_policy_t * policy); + +/** + * Given a policy, return 1 if the policy within is MLS, 0 if not. If + * it cannot be determined or upon error, return < 0. + * + * @param p Policy to which check. + * @return 1 if policy is MLS, 0 if not, < 0 upon error. + */ + extern int apol_policy_is_mls(const apol_policy_t * p); + +/** + * Given a policy, allocate and return a string that describes the + * policy (policy version, source/binary, mls/non-mls). + * + * @param p Policy to check. + * @return String that describes policy, or NULL upon error. The + * caller must free() this afterwards. + */ + extern char *apol_policy_get_version_type_mls_str(const apol_policy_t * p); + +#define APOL_MSG_ERR 1 +#define APOL_MSG_WARN 2 +#define APOL_MSG_INFO 3 + +/** + * Write a message to the callback stored within an apol error + * handler. If the msg_callback field is empty, then the default + * message callback will be used. + * + * @param p Error reporting handler. If NULL then write message to + * stderr. + * @param level Severity of message, one of APOL_MSG_ERR, + * APOL_MSG_WARN, or APOL_MSG_INFO. + * @param fmt Format string to print, using syntax of printf(3). + */ + extern void apol_handle_msg(const apol_policy_t * p, int level, const char *fmt, ...); + + __attribute__ ((format(printf, 3, 4))) extern void apol_handle_msg(const apol_policy_t * p, int level, const char *fmt, + ...); + +/** + * Invoke a apol_policy_t's callback for an error, passing it a format + * string and arguments. + */ +#define ERR(p, format, ...) apol_handle_msg(p, APOL_MSG_ERR, format, __VA_ARGS__) + +/** + * Invoke a apol_policy_t's callback for a warning, passing it a + * format string and arguments. + */ +#define WARN(p, format, ...) apol_handle_msg(p, APOL_MSG_WARN, format, __VA_ARGS__) + +/** + * Invoke a apol_policy_t's callback for an informational messag, + * passing it a format string and arguments. + */ +#define INFO(p, format, ...) apol_handle_msg(p, APOL_MSG_INFO, format, __VA_ARGS__) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/range_trans-query.h b/libapol/include/apol/range_trans-query.h new file mode 100644 index 0000000..e3113c5 --- /dev/null +++ b/libapol/include/apol/range_trans-query.h @@ -0,0 +1,198 @@ +/** + * @file + * + * Routines to query range transition rules of a policy. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_RANGE_TRANS_QUERY_H +#define APOL_RANGE_TRANS_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "mls_range.h" +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + + typedef struct apol_range_trans_query apol_range_trans_query_t; + +/** + * Execute a query against all range transition rules within the + * policy. + * + * @param p Policy within which to look up terules. + * @param r Structure containing parameters for query. If this is + * NULL then return all range transitions. + * @param v Reference to a vector of qpol_range_trans_t. The vector + * will be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon + * error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_range_trans_get_by_query(const apol_policy_t * p, const apol_range_trans_query_t * r, apol_vector_t ** v); + +/** + * Allocate and return a new range trans query structure. All fields + * are initialized, such that running this blank query results in + * returning all range transitions within the policy. The caller must + * call apol_range_trans_query_destroy() upon the return value + * afterwards. + * + * @return An initialized range trans structure, or NULL upon error. + */ + extern apol_range_trans_query_t *apol_range_trans_query_create(void); + +/** + * Deallocate all memory associated with the referenced range trans + * query, and then set it to NULL. This function does nothing if the + * query is already NULL. + * + * @param r Reference to a range trans query structure to destroy. + */ + extern void apol_range_trans_query_destroy(apol_range_trans_query_t ** r); + +/** + * Set a range trans query to return rules whose source symbol matches + * symbol. Symbol may be a type or attribute; if it is an alias then + * the query will convert it to its primary prior to searching. If + * is_indirect is non-zero then the search will be done indirectly. + * If the symbol is a type, then the query matches rules with one of + * the type's attributes. If the symbol is an attribute, then it + * matches rule with any of the attribute's types. + * + * @param p Policy handler, to report errors. + * @param r Range trans rule query to set. + * @param symbol Limit query to rules with this symbol as their + * source, or NULL to unset this field. + * @param is_indirect If non-zero, perform indirect matching. + * + * @return 0 on success, negative on error. + */ + extern int apol_range_trans_query_set_source(const apol_policy_t * p, apol_range_trans_query_t * r, const char *symbol, + int is_indirect); + +/** + * Set a range trans query to return rules whose target symbol matches + * symbol. Symbol may be a type or attribute; if it is an alias then + * the query will convert it to its primary prior to searching. If + * is_indirect is non-zero then the search will be done indirectly. + * If the symbol is a type, then the query matches rules with one of + * the type's attributes. If the symbol is an attribute, then it + * matches rule with any of the attribute's types. + * + * @param p Policy handler, to report errors. + * @param r Range trans query to set. + * @param symbol Limit query to rules with this symbol as their + * target, or NULL to unset this field. + * @param is_indirect If non-zero, perform indirect matching. + * + * @return 0 on success, negative on error. + */ + extern int apol_range_trans_query_set_target(const apol_policy_t * p, apol_range_trans_query_t * r, const char *symbol, + int is_indirect); + +/** + * Set a range trans query to return rules whose object class matches + * symbol. If more than one class are appended to the query, the + * rule's class must be one of those appended. (I.e., the rule's + * class must be a member of the query's classes.) Pass a NULL to + * clear all classes. Note that this performs straight string + * comparison, ignoring the regex flag. + * + * @param p Policy handler, to report errors. + * @param r Range trans query to set. + * @param obj_class Name of object class to add to search set, or NULL + * to clear all classes. + * + * @return 0 on success, negative on error. + */ + extern int apol_range_trans_query_append_class(const apol_policy_t * p, apol_range_trans_query_t * r, + const char *obj_class); + +/** + * Set a range trans query to return only rules matching a MLS range. + * This function takes ownership of the range, such that the caller + * must not modify nor destroy it afterwards. + * + * @param p Policy handler, to report errors. + * @param r Range trans query to set. + * @param range Limit query to only rules matching this range, or NULL + * to unset this field. + * @param range_match Specifies how to match a rules to a range. This + * must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or + * APOL_QUERY_EXACT. This parameter is ignored if range is NULL. + * + * @return Always returns 0. + */ + extern int apol_range_trans_query_set_range(const apol_policy_t * p, + apol_range_trans_query_t * r, apol_mls_range_t * range, + unsigned int range_match); + +/** + * Set a range trans query to treat the source symbol as any. That + * is, use the same symbol for either source or target of a rule. + * This flag does nothing if the source symbol is not set. + * + * @param p Policy handler, to report errors. + * @param r Range trans rule query to set. + * @param is_any Non-zero to use source symbol for any field, 0 to + * keep source as only source. + * + * @return Always 0. + */ + extern int apol_range_trans_query_set_source_any(const apol_policy_t * p, apol_range_trans_query_t * r, int is_any); + +/** + * Set a range trans query to use regular expression searching for + * source and target types/attributes. Strings will be treated as + * regexes instead of literals. Matching will occur against the type + * name or any of its aliases. + * + * @param p Policy handler, to report errors. + * @param r Range trans rule query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_range_trans_query_set_regex(const apol_policy_t * p, apol_range_trans_query_t * t, int is_regex); + +/** + * Render a range transition to a string. + * + * @param policy Policy handler, to report errors. + * @param rule The rule to render. + * + * @return a newly malloc()'d string representation of the rule, or NULL on + * failure; if the call fails, errno will be set. The caller is responsible + * for calling free() on the returned string. + */ + extern char *apol_range_trans_render(const apol_policy_t * policy, const qpol_range_trans_t * rule); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/rbacrule-query.h b/libapol/include/apol/rbacrule-query.h new file mode 100644 index 0000000..61822ee --- /dev/null +++ b/libapol/include/apol/rbacrule-query.h @@ -0,0 +1,279 @@ +/** + * @file + * + * Routines to query (role) allow and role_transition rules of a + * policy. This does not include access vector's allow rules, which + * are found in avrule-query.h. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_RBACRULE_QUERY_H +#define APOL_RBACRULE_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + + typedef struct apol_role_allow_query apol_role_allow_query_t; + typedef struct apol_role_trans_query apol_role_trans_query_t; + +/******************** (role) allow queries ********************/ + +/** + * Execute a query against all (role) allow rules within the policy. + * + * @param p Policy within which to look up allow rules. + * @param r Structure containing parameters for query. If this is + * NULL then return all allow rules. + * @param v Reference to a vector of qpol_role_allow_t. The vector + * will be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_role_allow_get_by_query(const apol_policy_t * p, const apol_role_allow_query_t * r, apol_vector_t ** v); + +/** + * Allocate and return a new role allow query structure. All fields + * are initialized, such that running this blank query results in + * returning all (role) allows within the policy. The caller must + * call apol_role_allow_query_destroy() upon the return value + * afterwards. + * + * @return An initialized role allow query structure, or NULL upon + * error. + */ + extern apol_role_allow_query_t *apol_role_allow_query_create(void); + +/** + * Deallocate all memory associated with the referenced role allow + * query, and then set it to NULL. This function does nothing if the + * query is already NULL. + * + * @param r Reference to a role allow query structure to destroy. + */ + extern void apol_role_allow_query_destroy(apol_role_allow_query_t ** r); + +/** + * Set a role allow query to return rules with a particular source + * role. + * + * @param p Policy handler, to report errors. + * @param r Role allow query to set. + * @param role Limit query to rules with this role as their source, or + * NULL to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_role_allow_query_set_source(const apol_policy_t * p, apol_role_allow_query_t * r, const char *role); + +/** + * Set a role allow query to return rules with a particular target + * role. This field is ignored if + * apol_role_allow_query_set_source_any() is set to non-zero. + * + * @param p Policy handler, to report errors. + * @param r Role allow query to set. + * @param role Limit query to rules with this role as their target, or + * NULL to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_role_allow_query_set_target(const apol_policy_t * p, apol_role_allow_query_t * r, const char *role); + +/** + * Set a role allow query to treat the source role as any. That is, + * use the same symbol for either source or target of a (role) allow + * rule. This flag does nothing if the source role is not set. + * + * @param p Policy handler, to report errors. + * @param r Role allow query to set. + * @param is_any Non-zero to use source symbol for any field, 0 to + * keep source as only source. + * + * @return Always 0. + */ + extern int apol_role_allow_query_set_source_any(const apol_policy_t * p, apol_role_allow_query_t * r, int is_any); + +/** + * Set a role allow query to use regular expression searching for + * source and target fields. Strings will be treated as regexes + * instead of literals. + * + * @param p Policy handler, to report errors. + * @param r Role allow query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_role_allow_query_set_regex(const apol_policy_t * p, apol_role_allow_query_t * r, int is_regex); + +/** + * Render a role allow rule to a string. + * + * @param policy Policy handler, to report errors. + * @param rule The rule to render. + * + * @return a newly malloc()'d string representation of the rule, or NULL on + * failure; if the call fails, errno will be set. The caller is responsible + * for calling free() on the returned string. + */ + extern char *apol_role_allow_render(const apol_policy_t * policy, const qpol_role_allow_t * rule); + +/******************** role_transition queries ********************/ + +/** + * Execute a query against all role_transition rules within the + * policy. + * + * @param p Policy within which to look up role_transition rules. + * @param r Structure containing parameters for query. If this is + * NULL then return all role_transition rules. + * @param v Reference to a vector of qpol_role_trans_t. The vector + * will be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_role_trans_get_by_query(const apol_policy_t * p, const apol_role_trans_query_t * r, apol_vector_t ** v); + +/** + * Allocate and return a new role trans query structure. All fields + * are initialized, such that running this blank query results in + * returning all role_transitions within the policy. The caller must + * call apol_role_trans_query_destroy() upon the return value + * afterwards. + * + * @return An initialized role trans query structure, or NULL upon + * error. + */ + extern apol_role_trans_query_t *apol_role_trans_query_create(void); + +/** + * Deallocate all memory associated with the referenced role trans + * query, and then set it to NULL. This function does nothing if the + * query is already NULL. + * + * @param r Reference to a role trans query structure to destroy. + */ + extern void apol_role_trans_query_destroy(apol_role_trans_query_t ** r); + +/** + * Set a role trans query to return rules with a particular source + * role. + * + * @param p Policy handler, to report errors. + * @param r Role trans query to set. + * @param role Limit query to rules with this role as their source, or + * NULL to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_role_trans_query_set_source(const apol_policy_t * p, apol_role_trans_query_t * r, const char *role); + +/** + * Set a role trans query to return rules with a particular target + * symbol. Symbol may be a type or attribute; if it is an alias then + * the query will convert it to its primary prior to searching. If + * is_indirect is non-zero then the search will be done indirectly. + * If the symbol is a type, then the query matches rules with one of + * the type's attributes. If the symbol is an attribute, then it + * matches rule with any of the attribute's types. + * + * @param p Policy handler, to report errors. + * @param r Role trans query to set. + * @param symbol Limit query to rules with this type or attribute as + * their target, or NULL to unset this field. + * @param is_indirect If non-zero, perform indirect matching. + * + * @return 0 on success, negative on error. + */ + extern int apol_role_trans_query_set_target(const apol_policy_t * p, apol_role_trans_query_t * r, const char *symbol, + int is_indirect); + +/** + * Set a role trans query to return rules with a particular default + * role. This field is ignored if + * apol_role_trans_query_set_source_any() is set to non-zero. + * + * @param p Policy handler, to report errors. + * @param r Role trans query to set. + * @param role Limit query to rules with this role as their default, or + * NULL to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_role_trans_query_set_default(const apol_policy_t * p, apol_role_trans_query_t * r, const char *role); + +/** + * Set a role trans query to treat the source role as any. That is, + * use the same symbol for either source or default of a + * role_transition rule. This flag does nothing if the source role is + * not set. Note that a role_transition's target is a type, so thus + * this flag does not affect its searching. + * + * @param p Policy handler, to report errors. + * @param r Role trans query to set. + * @param is_any Non-zero to use source symbol for source or default + * field, 0 to keep source as only source. + * + * @return Always 0. + */ + extern int apol_role_trans_query_set_source_any(const apol_policy_t * p, apol_role_trans_query_t * r, int is_any); + +/** + * Set a role trans query to use regular expression searching for + * source, target, and default fields. Strings will be treated as + * regexes instead of literals. For the target type, matching will + * occur against the type name or any of its aliases. + * + * @param p Policy handler, to report errors. + * @param r Role trans query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_role_trans_query_set_regex(const apol_policy_t * p, apol_role_trans_query_t * r, int is_regex); + +/** + * Render a role_transition rule to a string. + * + * @param policy Policy handler, to report errors. + * @param rule The rule to render. + * + * @return A newly malloc()'d string representation of the rule, or NULL on + * failure; if the call fails, errno will be set. The caller is responsible + * for calling free() on the returned string. + */ + extern char *apol_role_trans_render(const apol_policy_t * policy, const qpol_role_trans_t * rule); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/relabel-analysis.h b/libapol/include/apol/relabel-analysis.h new file mode 100644 index 0000000..1aed049 --- /dev/null +++ b/libapol/include/apol/relabel-analysis.h @@ -0,0 +1,258 @@ +/** + * @file + * + * Routines to perform a direct relabelling analysis. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_RELABEL_ANALYSIS_H +#define APOL_RELABEL_ANALYSIS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + +/* defines for direction flag */ +#define APOL_RELABEL_DIR_TO 0x01 +#define APOL_RELABEL_DIR_FROM 0x02 +#define APOL_RELABEL_DIR_BOTH (APOL_RELABEL_DIR_TO|APOL_RELABEL_DIR_FROM) +#define APOL_RELABEL_DIR_SUBJECT 0x04 + + typedef struct apol_relabel_analysis apol_relabel_analysis_t; + typedef struct apol_relabel_result apol_relabel_result_t; + typedef struct apol_relabel_result_pair apol_relabel_result_pair_t; + +/******************** functions to do relabel analysis ********************/ + +/** + * Execute a relabel analysis against a particular policy. + * + * @param p Policy within which to look up allow rules. + * @param r A non-NULL structure containing parameters for analysis. + * @param v Reference to a vector of apol_relabel_result_t. The + * vector will be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success, negative on error. + */ + extern int apol_relabel_analysis_do(const apol_policy_t * p, apol_relabel_analysis_t * r, apol_vector_t ** v); + +/** + * Allocate and return a new relabel analysis structure. All fields + * are cleared; one must fill in the details of the analysis before + * running it. The caller must call apol_relabel_analysis_destroy() + * upon the return value afterwards. + * + * @return An initialized relabel analysis structure, or NULL upon + * error. + */ + extern apol_relabel_analysis_t *apol_relabel_analysis_create(void); + +/** + * Deallocate all memory associated with the referenced relabel + * analysis, and then set it to NULL. This function does nothing if + * the analysis is already NULL. + * + * @param r Reference to a relabel analysis structure to destroy. + */ + extern void apol_relabel_analysis_destroy(apol_relabel_analysis_t ** r); + +/** + * Set a relabel analysis to search in a specific direction. This + * function must be called prior to running the analysis. + * + * @param p Policy handler, to report errors. + * @param r Relabel analysis to set. + * @param dir Direction to analyze, one of the APOL_RELABEL_DIR_TO, + * APOL_RELABEL_DIR_FROM, APOL_RELABEL_DIR_BOTH, or + * APOL_RELABEL_DIR_SUBJECT. + * + * @return 0 on success, negative on error. + */ + extern int apol_relabel_analysis_set_dir(const apol_policy_t * p, apol_relabel_analysis_t * r, unsigned int dir); + +/** + * Set a relabel analysis to begin searching using a given type. This + * function must be called prior to running the analysis. + * + * @param p Policy handler, to report errors. + * @param r Relabel anlysis to set. + * @param name Begin searching types with this non-NULL name. + * + * @return 0 on success, negative on error. + */ + extern int apol_relabel_analysis_set_type(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *name); + +/** + * Set a relabel analysis to return rules with this object + * (non-common) class. If more than one class is appended to the + * query, the rule's class must be one of those appended. (I.e., the + * rule's class must be a member of the analysis's classes.) Pass a + * NULL to clear all classes. + * + * @param p Policy handler, to report errors. + * @param r Relabel analysis to set. + * @param class Name of object class to add to search set, or NULL to + * clear all classes. + * + * @return 0 on success, negative on error. + */ + extern int apol_relabel_analysis_append_class(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *obj_class); + +/** + * Set a relabel analysis to return rules with this subject as their + * source type. If more than one subject is appended to the query, + * the rule's source must be one of those appended. (I.e., the rule's + * source must be a member of the analysis's subject.) Pass a NULL to + * clear all types. Note that these subjects are ignored when doing + * subject relabel analysis. + * + * @param p Policy handler, to report errors. + * @param r Relabel analysis to set. + * @param subject Name of type to add to search set, or NULL to clear + * all subjects. + * + * @return 0 on success, negative on error. + */ + extern int apol_relabel_analysis_append_subject(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *subject); + +/** + * Set a relabel analysis to return only types matching a regular + * expression. Note that the regexp will also match types' aliases. + * + * @param p Policy handler, to report errors. + * @param r Relabel anlysis to set. + * @param result Only return types matching this regular expression, or + * NULL to return all types + * + * @return 0 on success, negative on error. + */ + extern int apol_relabel_analysis_set_result_regex(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *result); + +/******************** functions to access relabel results ********************/ + +/** + * Return the relabelto vector embedded within an apol_relabel_result + * node. This is a vector of apol_relabel_result_pair_t objects. The + * caller shall not call apol_vector_destroy() upon this pointer. + * + * @param r Relabel result node. + * + * @return Pointer to a vector of rule pairs, relative to the policy + * originally used to generate the relabelling result. + */ + extern const apol_vector_t *apol_relabel_result_get_to(const apol_relabel_result_t * r); + +/** + * Return the relabelfrom vector embedded within an + * apol_relabel_result node. This is a vector of + * apol_relabel_result_pair_t objects. The caller shall not call + * apol_vector_destroy() upon this pointer. + * + * @param r Relabel result node. + * + * @return Pointer to a vector of rule pairs, relative to the policy + * originally used to generate the relabelling result. + */ + extern const apol_vector_t *apol_relabel_result_get_from(const apol_relabel_result_t * r); + +/** + * Return the relabelboth vector embedded within an + * apol_relabel_result node. This is a vector of + * apol_relabel_result_pair_t objects. The caller shall not call + * apol_vector_destroy() upon this pointer. + * + * @param r Relabel result node. + * + * @return Pointer to a vector of rule pairs, relative to the policy + * originally used to generate the relabelling result. + */ + extern const apol_vector_t *apol_relabel_result_get_both(const apol_relabel_result_t * r); + +/** + * Return the resulting type for an apol_relabel_result node. + * + * @param r Relabel result node. + * + * @return Pointer to a result type. + */ + extern const qpol_type_t *apol_relabel_result_get_result_type(const apol_relabel_result_t * r); + +/** + * Return the first rule from an apol_relabel_result_pair object. + * + * For object mode analysis, this is the rule that affects the + * starting type. Either that type or one of its attributes will be + * the target type for the returned rule. + * + * For subject mode analysis, this is a rule affects the starting + * subject. Either that subject or one of its attributes will be the + * source type for the returned rule. + * + * @param p Relabel result pair object. + * + * @return Rule affecting the starting type/subject. + */ + extern const qpol_avrule_t *apol_relabel_result_pair_get_ruleA(const apol_relabel_result_pair_t * p); + +/** + * Return the other rule from an apol_relabel_result_pair object. + * + * For object mode analysis, this is the rule that affects the + * resulting type. Either that type or one of its attributes will be + * the target type for the returned rule. + * + * For subject mode analysis, the returned pointer will be NULL. + * + * @param p Relabel result pair object. + * + * @return Rule affecting the resulting type/subject (for object mode) + * or NULL (for subject mode). + */ + extern const qpol_avrule_t *apol_relabel_result_pair_get_ruleB(const apol_relabel_result_pair_t * p); + +/** + * Return the intermediate type for an apol_relabel_result_pair + * object. + * + * For object mode analysis, this is the source type for the first + * rule; it also will be the source type for the other rule. + * + * For subject mode analysis, the returned pointer will be NULL. + * + * @param p Relabel result pair object. + * + * @return Intermediate type for relabel result (for object mode) or + * NULL (for subject mode). + */ + extern const qpol_type_t *apol_relabel_result_pair_get_intermediate_type(const apol_relabel_result_pair_t * p); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/render.h b/libapol/include/apol/render.h new file mode 100644 index 0000000..0580813 --- /dev/null +++ b/libapol/include/apol/render.h @@ -0,0 +1,82 @@ +/** + * @file + * + * Public interfaces that renders things that are not already covered + * by one of the query files. Unless otherwise stated, all functions + * return a newly allocated string, which the caller is responsible + * for free()ing afterwards. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author David Windsor dwindsor@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_RENDER_H +#define APOL_RENDER_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "mls-query.h" +#include <qpol/policy.h> +#include <stdlib.h> + +/** + * Given an IPv4 address (or mask) in qpol byte order, allocate and + * return a string representing that address. + * + * @param p Reference to a policy, for reporting errors + * @param addr Address (or mask) to render. + * + * @return A newly allocated string, which the caller must free. + * Returns NULL on error. + */ + extern char *apol_ipv4_addr_render(const apol_policy_t * p, uint32_t addr[4]); + +/** + * Given an IPv6 address (or mask) in qpol byte order, allocate and + * return a string representing that address. + * + * @param p Reference to a policy, for reporting errors + * @param addr Address (or mask) to render. + * + * @return A newly allocated string, which the caller must free. + * Returns NULL on error. + */ + extern char *apol_ipv6_addr_render(const apol_policy_t * p, uint32_t addr[4]); + +/** + * Creates a string containing the textual representation of + * a security context. + * @param p Reference to a policy. + * @param context Reference to the security context to be rendered. + * + * @return A newly allocated string on success, caller must free; + * NULL on error. + */ + extern char *apol_qpol_context_render(const apol_policy_t * p, const qpol_context_t * context); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/role-query.h b/libapol/include/apol/role-query.h new file mode 100644 index 0000000..ad80490 --- /dev/null +++ b/libapol/include/apol/role-query.h @@ -0,0 +1,127 @@ +/** + * @file + * Public Interface for querying roles of a policy. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_ROLE_QUERY_H +#define APOL_ROLE_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + + typedef struct apol_role_query apol_role_query_t; + +/******************** role queries ********************/ + +/** + * Execute a query against all roles within the policy. + * + * @param p Policy within which to look up roles. + * @param r Structure containing parameters for query. If this is + * NULL then return all roles. + * @param v Reference to a vector of qpol_role_t. The vector will be + * allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_role_get_by_query(const apol_policy_t * p, apol_role_query_t * r, apol_vector_t ** v); + +/** + * Allocate and return a new role query structure. All fields are + * initialized, such that running this blank query results in + * returning all roles within the policy. The caller must call + * apol_role_query_destroy() upon the return value afterwards. + * + * @return An initialized role query structure, or NULL upon error. + */ + extern apol_role_query_t *apol_role_query_create(void); + +/** + * Deallocate all memory associated with the referenced role query, + * and then set it to NULL. This function does nothing if the query + * is already NULL. + * + * @param r Reference to a role query structure to destroy. + */ + extern void apol_role_query_destroy(apol_role_query_t ** r); + +/** + * Set a role query to return only roles that match this name. This + * function duplicates the incoming name. + * + * @param p Policy handler, to report errors. + * @param r Role query to set. + * @param name Limit query to only roles with this name, or NULL to + * unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_role_query_set_role(const apol_policy_t * p, apol_role_query_t * r, const char *name); + +/** + * Set a role query to return only roles containing this type or one + * of its aliases. This function duplicates the incoming name. + * + * @param p Policy handler, to report errors. + * @param r Role query to set. + * @param name Limit query to only roles with this type, or NULL to + * unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_role_query_set_type(const apol_policy_t * p, apol_role_query_t * r, const char *name); + +/** + * Set a role query to use regular expression searching for all of its + * fields. Strings will be treated as regexes instead of literals. + * + * @param p Policy handler, to report errors. + * @param r Role query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_role_query_set_regex(const apol_policy_t * p, apol_role_query_t * r, int is_regex); + +/** + * See if the role passed in includes the type that is the + * second parameter. + * @param p Policy handler, to report errors. + * @param r Role to check if type is included in it. + * @param t Type that is checked against all types that are in role + * @return 1 if the type is included in the role, 0 if it's not, < 0 on error +*/ + extern int apol_role_has_type(const apol_policy_t * p, const qpol_role_t * r, const qpol_type_t * t); + +#ifdef __cplusplus +} +#endif + +#endif /* APOL_ROLE_QUERY_H */ diff --git a/libapol/include/apol/terule-query.h b/libapol/include/apol/terule-query.h new file mode 100644 index 0000000..4b1e474 --- /dev/null +++ b/libapol/include/apol/terule-query.h @@ -0,0 +1,321 @@ +/** + * @file + * + * Routines to query type enforcement rules of a policy. These are + * type_transition, type_member, and type_change rules. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_TERULE_QUERY_H +#define APOL_TERULE_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + + typedef struct apol_terule_query apol_terule_query_t; + +/** + * Execute a query against all type enforcement rules within the policy. + * + * @param p Policy within which to look up terules. + * @param t Structure containing parameters for query. If this is + * NULL then return all terules. + * @param v Reference to a vector of qpol_terule_t. The vector will + * be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_terule_get_by_query(const apol_policy_t * p, const apol_terule_query_t * t, apol_vector_t ** v); + +/** + * Execute a query against all syntactic type enforcement rules within + * the policy. If the policy has line numbers, then the returned list + * will be sorted increasingly by line number. + * + * @param p Policy within which to look up terules. <b>Must be a + * source policy.</b> + * @param t Structure containing parameters for query. If this is + * NULL then return all terules. + * @param v Reference to a vector of qpol_syn_terule_t. The vector + * will be allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_syn_terule_get_by_query(const apol_policy_t * p, const apol_terule_query_t * t, apol_vector_t ** v); + +/** + * Allocate and return a new terule query structure. All fields are + * initialized, such that running this blank query results in + * returning all terules within the policy. The caller must call + * apol_terule_query_destroy() upon the return value afterwards. + * + * @return An initialized terule query structure, or NULL upon error. + */ + extern apol_terule_query_t *apol_terule_query_create(void); + +/** + * Deallocate all memory associated with the referenced terule query, + * and then set it to NULL. This function does nothing if the query + * is already NULL. + * + * @param t Reference to a terule query structure to destroy. + */ + extern void apol_terule_query_destroy(apol_terule_query_t ** t); + +/** + * Set a terule query to search only certain type enforcement rules + * within the policy. This is a bitmap; use the constants in + * libqpol/terule_query.h (QPOL_RULE_TYPE_TRANS, etc.) to give the + * rule selections. + * + * @param p Policy handler, to report errors. + * @param te TE rule query to set. + * @param rules Bitmap to indicate which rules to search, or 0 to + * search all rules. + * + * @return Always 0. + */ + extern int apol_terule_query_set_rules(const apol_policy_t * p, apol_terule_query_t * t, unsigned int rules); + +/** + * Set a terule query to return rules whose source symbol matches + * symbol. Symbol may be a type or attribute; if it is an alias then + * the query will convert it to its primary prior to searching. If + * is_indirect is non-zero then the search will be done indirectly. + * If the symbol is a type, then the query matches rules with one of + * the type's attributes. If the symbol is an attribute, then it + * matches rule with any of the attribute's types. + * + * @param p Policy handler, to report errors. + * @param t TE rule query to set. + * @param symbol Limit query to rules with this symbol as their + * source, or NULL to unset this field. + * @param is_indirect If non-zero, perform indirect matching. + * + * @return 0 on success, negative on error. + */ + extern int apol_terule_query_set_source(const apol_policy_t * p, apol_terule_query_t * t, const char *symbol, + int is_indirect); + +/** + * Set an terule query to return rules whose source symbol is matched as a type + * or an attribute. The symbol will match both types and attributes by default. + * @see apol_avrule_query_set_source() to set the symbol to match. + * + * @param p Policy handler, to report errors. + * @param t TE rule query to set. + * @param component Bit-wise or'ed set of APOL_QUERY_SYMBOL_IS_TYPE + * and APOL_QUERY_SYMBOL_IS_ATTRIBUTE indicating the type of component + * to match. + * + * @return 0 on success, negative on error. + */ + extern int apol_terule_query_set_source_component(const apol_policy_t * p, apol_terule_query_t * t, unsigned int component); + +/** + * Set a terule query to return rules whose target symbol matches + * symbol. Symbol may be a type or attribute; if it is an alias then + * the query will convert it to its primary prior to searching. If + * is_indirect is non-zero then the search will be done indirectly. + * If the symbol is a type, then the query matches rules with one of + * the type's attributes. If the symbol is an attribute, then it + * matches rule with any of the attribute's types. + * + * @param p Policy handler, to report errors. + * @param t TE rule query to set. + * @param symbol Limit query to rules with this symbol as their + * target, or NULL to unset this field. + * @param is_indirect If non-zero, perform indirect matching. + * + * @return 0 on success, negative on error. + */ + extern int apol_terule_query_set_target(const apol_policy_t * p, apol_terule_query_t * t, const char *symbol, + int is_indirect); + +/** + * Set an terule query to return rules whose target symbol is matched as a type + * or an attribute. The symbol will match both types and attributes by default. + * @see apol_avrule_query_set_source() to set the symbol to match. + * + * @param p Policy handler, to report errors. + * @param t TE rule query to set. + * @param component Bit-wise or'ed set of APOL_QUERY_SYMBOL_IS_TYPE + * and APOL_QUERY_SYMBOL_IS_ATTRIBUTE indicating the type of component + * to match. + * + * @return 0 on success, negative on error. + */ + extern int apol_terule_query_set_target_component(const apol_policy_t * p, apol_terule_query_t * t, unsigned int component); + +/** + * Set a terule query to return rules with this default type. The + * symbol may be a type or any of its aliases; it may not be an + * attribute. + * + * @param p Policy handler, to report errors. + * @param t TE rule query to set. + * @param type Name of default type to search. + * + * @return 0 on success, negative on error. + */ + extern int apol_terule_query_set_default(const apol_policy_t * p, apol_terule_query_t * t, const char *type); + +/** + * Set at terule query to return rules with this object (non-common) + * class. If more than one class are appended to the query, the + * rule's class must be one of those appended. (I.e., the rule's + * class must be a member of the query's classes.) Pass a NULL to + * clear all classes. Note that this performs straight string + * comparison, ignoring the regex flag. + + * + * @param p Policy handler, to report errors. + * @param t TE rule query to set. + * @param obj_class Name of object class to add to search set. + * + * @return 0 on success, negative on error. + */ + extern int apol_terule_query_append_class(const apol_policy_t * p, apol_terule_query_t * t, const char *obj_class); + +/** + * Set a terule query to return rules that are in conditionals and + * whose conditional uses a particular boolean variable. + * Unconditional rules will not be returned. + * + * @param p Policy handler, to report errors. + * @param t TE rule query to set. + * @param bool_name Name of boolean that conditional must contain. If + * NULL then search all rules. + * + * @return 0 on success, negative on error. + */ + extern int apol_terule_query_set_bool(const apol_policy_t * p, apol_terule_query_t * t, const char *bool_name); + +/** + * Set a terule query to search only enabled rules within the policy. + * These include rules that are unconditional and those within enabled + * conditionals. + * + * @param p Policy handler, to report errors. + * @param t TE rule query to set. + * @param is_enabled Non-zero to search only enabled rules, 0 to + * search all rules. + * + * @return Always 0. + */ + extern int apol_terule_query_set_enabled(const apol_policy_t * p, apol_terule_query_t * t, int is_enabled); + +/** + * Set a terule query to treat the source symbol as any. That is, use + * the same symbol for either source, target, or default of a rule. + * This flag does nothing if the source symbol is not set. + * + * @param p Policy handler, to report errors. + * @param t TE rule query to set. + * @param is_any Non-zero to use source symbol for any field, 0 to + * keep source as only source. + * + * @return Always 0. + */ + extern int apol_terule_query_set_source_any(const apol_policy_t * p, apol_terule_query_t * t, int is_any); + +/** + * Set a terule query to use regular expression searching for source + * and target types/attributes and default type. Strings will be + * treated as regexes instead of literals. Matching will occur against + * the type name or any of its aliases. + * + * @param p Policy handler, to report errors. + * @param t TE rule query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_terule_query_set_regex(const apol_policy_t * p, apol_terule_query_t * t, int is_regex); + +/** + * Given a single terule, return a newly allocated vector of + * qpol_syn_terule_t pointers (relative to the given policy) which + * comprise that rule. The vector will be sorted by line numbers if + * the policy has line numbers. + * + * @param p Policy from which to obtain syntactic rules. + * @param rule TE rule to convert. + * + * @return A newly allocated vector of syn_terule_t pointers. The + * caller is responsible for calling apol_vector_destroy() afterwards. + */ + extern apol_vector_t *apol_terule_to_syn_terules(const apol_policy_t * p, const qpol_terule_t * rule); + +/** + * Given a vector of terules (qpol_terule_t pointers), return a newly + * allocated vector of qpol_syn_terule_t pointers (relative to the + * given policy) which comprise all of those rules. The returned + * vector will be sorted by line numbers if the policy has line + * numbers. Also, it will not have any duplicate syntactic rules. + * + * @param p Policy from which to obtain syntactic rules. + * @param rules Vector of TE rules to convert. + * + * @return A newly allocated vector of syn_terule_t pointers. The + * caller is responsible for calling apol_vector_destroy() afterwards. + */ + extern apol_vector_t *apol_terule_list_to_syn_terules(const apol_policy_t * p, const apol_vector_t * rules); + +/** + * Render a terule to a string. + * + * @param policy Policy handler, to report errors. + * @param rule The rule to render. + * + * @return a newly malloc()'d string representation of the rule, or NULL on + * failure; if the call fails, errno will be set. The caller is responsible + * for calling free() on the returned string. + */ + extern char *apol_terule_render(const apol_policy_t * policy, const qpol_terule_t * rule); + +/** + * Render a syntactic terule to a string. + * + * @param policy Policy handler to report errors. + * @param rule The rule to render. + * + * @return a newly malloc()'d string representation of the rule, or NULL on + * failure; if the call fails, errno will be set. The caller is responsible + * for calling free() on the returned string. +*/ + extern char *apol_syn_terule_render(const apol_policy_t * policy, const qpol_syn_terule_t * rule); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/type-query.h b/libapol/include/apol/type-query.h new file mode 100644 index 0000000..a571b19 --- /dev/null +++ b/libapol/include/apol/type-query.h @@ -0,0 +1,172 @@ +/** + * @file + * + * Routines to query types and attributes of a policy. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_TYPE_QUERY_H +#define APOL_TYPE_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + + typedef struct apol_type_query apol_type_query_t; + typedef struct apol_attr_query apol_attr_query_t; + +/******************** type queries ********************/ + +/** + * Execute a query against all types within the policy. The results + * will only contain types, not aliases nor attributes. + * + * @param p Policy within which to look up types. + * @param t Structure containing parameters for query. If this is + * NULL then return all types. + * @param v Reference to a vector of qpol_type_t. The vector will be + * allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_type_get_by_query(const apol_policy_t * p, apol_type_query_t * t, apol_vector_t ** v); + +/** + * Allocate and return a new type query structure. All fields are + * initialized, such that running this blank query results in + * returning all types within the policy. The caller must call + * apol_type_query_destroy() upon the return value afterwards. + * + * @return An initialized type query structure, or NULL upon error. + */ + extern apol_type_query_t *apol_type_query_create(void); + +/** + * Deallocate all memory associated with the referenced type query, + * and then set it to NULL. This function does nothing if the query + * is already NULL. + * + * @param t Reference to a type query structure to destroy. + */ + extern void apol_type_query_destroy(apol_type_query_t ** t); + +/** + * Set a type query to return only types that match this name. The + * name may be either a type or one of its aliases. This function + * duplicates the incoming name. + * + * @param p Policy handler, to report errors. + * @param t Type query to set. + * @param name Limit query to only types or aliases with this name, or + * NULL to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_type_query_set_type(const apol_policy_t * p, apol_type_query_t * t, const char *name); + +/** + * Set a type query to use regular expression searching for all of its + * fields. Strings will be treated as regexes instead of literals. + * Matching will occur against the type name or any of its aliases. + * + * @param p Policy handler, to report errors. + * @param t Type query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_type_query_set_regex(const apol_policy_t * p, apol_type_query_t * t, int is_regex); + +/******************** attribute queries ********************/ + +/** + * Execute a query against all attributes within the policy. The + * results will only contain attributes, not types nor aliases. + * + * @param p Policy within which to look up attributes. + * @param a Structure containing parameters for query. If this is + * NULL then return all attributes. + * @param v Reference to a vector of qpol_type_t. The vector will be + * allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_attr_get_by_query(const apol_policy_t * p, apol_attr_query_t * a, apol_vector_t ** v); + +/** + * Allocate and return a new attribute query structure. All fields + * are initialized, such that running this blank query results in + * returning all attributes within the policy. The caller must call + * apol_attr_query_destroy() upon the return value afterwards. + * + * @return An initialized attribute query structure, or NULL upon error. + */ + extern apol_attr_query_t *apol_attr_query_create(void); + +/** + * Deallocate all memory associated with the referenced attribute + * query, and then set it to NULL. This function does nothing if the + * query is already NULL. + * + * @param a Reference to an attribute query structure to destroy. + */ + extern void apol_attr_query_destroy(apol_attr_query_t ** a); + +/** + * Set an attribute query to return only attributes that match this + * name. This function duplicates the incoming name. + * + * @param p Policy handler, to report errors. + * @param a Attribute query to set. + * @param name Limit query to only attributes with this name, or NULL + * to unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_attr_query_set_attr(const apol_policy_t * p, apol_attr_query_t * a, const char *name); + +/** + * Set an attribute query to use regular expression searching for all + * of its fields. Strings will be treated as regexes instead of + * literals. + * + * @param p Policy handler, to report errors. + * @param a Attribute query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_attr_query_set_regex(const apol_policy_t * p, apol_attr_query_t * a, int is_regex); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/types-relation-analysis.h b/libapol/include/apol/types-relation-analysis.h new file mode 100644 index 0000000..1278072 --- /dev/null +++ b/libapol/include/apol/types-relation-analysis.h @@ -0,0 +1,380 @@ +/** + * @file + * + * Routines to perform a two-types relationship analysis. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_TYPES_RELATION_ANALYSIS_H +#define APOL_TYPES_RELATION_ANALYSIS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include <qpol/policy.h> + +/* Specify a which types relationship analysis/analyses to run using + * these bit values. + */ +#define APOL_TYPES_RELATION_COMMON_ATTRIBS 0x0001 +#define APOL_TYPES_RELATION_COMMON_ROLES 0x0002 +#define APOL_TYPES_RELATION_COMMON_USERS 0x0004 +#define APOL_TYPES_RELATION_SIMILAR_ACCESS 0x0010 +#define APOL_TYPES_RELATION_DISSIMILAR_ACCESS 0x0020 +#define APOL_TYPES_RELATION_ALLOW_RULES 0x0100 +#define APOL_TYPES_RELATION_TYPE_RULES 0x0200 +#define APOL_TYPES_RELATION_DOMAIN_TRANS_AB 0x0400 +#define APOL_TYPES_RELATION_DOMAIN_TRANS_BA 0x0800 +#define APOL_TYPES_RELATION_DIRECT_FLOW 0x1000 +#define APOL_TYPES_RELATION_TRANS_FLOW_AB 0x4000 +#define APOL_TYPES_RELATION_TRANS_FLOW_BA 0x8000 + + typedef struct apol_types_relation_analysis apol_types_relation_analysis_t; + typedef struct apol_types_relation_result apol_types_relation_result_t; + typedef struct apol_types_relation_access apol_types_relation_access_t; + +/********** functions to do types relation analysis **********/ + +/** + * Execute a two types relationship analysis against a particular + * policy. + * + * @param p Policy within which to look up relationships. + * @param tr A non-NULL structure containing parameters for analysis. + * @param r Reference to a apol_types_relation_result_t. The object + * will be allocated by this function. The caller must call + * apol_types_relation_result_destroy() afterwards. This will be set + * to NULL upon no results or upon error. + * + * @return 0 on success, negative on error. + */ + extern int apol_types_relation_analysis_do(apol_policy_t * p, + const apol_types_relation_analysis_t * tr, apol_types_relation_result_t ** r); + +/** + * Allocate and return a new two types relationship analysis + * structure. All fields are cleared; one must fill in the details of + * the analysis before running it. The caller must call + * apol_types_relation_analysis_destroy() upon the return value + * afterwards. + * + * @return An initialized two types relationship analysis structure, or + * NULL upon error. + */ + extern apol_types_relation_analysis_t *apol_types_relation_analysis_create(void); + +/** + * Deallocate all memory associated with the referenced types relation + * analysis, and then set it to NULL. This function does nothing if + * the analysis is already NULL. + * + * @param tr Reference to a types relation analysis structure to + * destroy. + */ + extern void apol_types_relation_analysis_destroy(apol_types_relation_analysis_t ** tr); + +/** + * Set a types relation analysis to begin analysis from this first + * type. This function must be called prior to running the analysis. + * + * @param p Policy handler, to report errors. + * @param tr Types relation analysis to set. + * @param name Perform analysis with this non-NULL name. + * + * @return 0 on success, negative on error. + */ + extern int apol_types_relation_analysis_set_first_type(const apol_policy_t * p, apol_types_relation_analysis_t * tr, + const char *name); + +/** + * Set a types relation analysis to begin analysis from this other + * type. This function must be called prior to running the analysis. + * + * @param p Policy handler, to report errors. + * @param tr Types relation analysis to set. + * @param name Perform analysis with this other non-NULL name. + * + * @return 0 on success, negative on error. + */ + extern int apol_types_relation_analysis_set_other_type(const apol_policy_t * p, apol_types_relation_analysis_t * tr, + const char *name); + +/** + * Set a types relation analysis to run the specified + * analysis/analyses. This is a bitmap; use the defines + * APOL_TYPES_RELATION_COMMON_ATTRIBUTES etc. to specify which one(s) + * to run. + * + * @param p Policy handler, to report errors. + * @param tr Types relation analysis to set. + * @param analyses Bitmap to indicate which analyses to run, or 0 to + * run them all. + * + * @return Always 0. + */ + extern int apol_types_relation_analysis_set_analyses(const apol_policy_t * p, apol_types_relation_analysis_t * tr, + unsigned int analyses); + +/*************** functions to access types relation results ***************/ + +/** + * Deallocate all memory associated with a types relation analysis + * result, including the pointer itself. This function does nothing + * if the result is already NULL. + * + * @param result Reference to a types relation result structure to + * destroy. + */ + extern void apol_types_relation_result_destroy(apol_types_relation_result_t ** result); + +/** + * Return the vector of attributes common to the two types. This is a + * vector of qpol_type_t pointers. The caller <b>should not</b> call + * apol_vector_destroy() upon the returned vector. If the user did + * not request this analysis then the return value will be NULL. + * + * @param result Types relation result from which to get common + * attributes. + * + * @return Vector of common attributes, or NULL if analysis was not + * run. + */ + extern const apol_vector_t *apol_types_relation_result_get_attributes(const apol_types_relation_result_t * result); + +/** + * Return the vector of roles common to the two types. This is a + * vector of qpol_role_t pointers. The caller <b>should not</b> call + * apol_vector_destroy() upon the returned vector. If the user did + * not request this analysis then the return value will be NULL. + * + * @param result Types relation result from which to get common roles. + * + * @return Vector of common roles, or NULL if analysis was not run. + */ + extern const apol_vector_t *apol_types_relation_result_get_roles(const apol_types_relation_result_t * result); + +/** + * Return the vector of users common to the two types. This is a + * vector of qpol_user_t pointers. The caller <b>should not</b> call + * apol_vector_destroy() upon the returned vector. If the user did + * not request this analysis then the return value will be NULL. + * + * @param result Types relation result from which to get common users. + * + * @return Vector of common users, or NULL if analysis was not run. + */ + extern const apol_vector_t *apol_types_relation_result_get_users(const apol_types_relation_result_t * result); + +/** + * Return the vector of accesses similar to the two types. This is a + * vector of apol_types_relation_access_t pointers. The vector will + * contain only the rules that the first type had. Call + * apol_types_relation_result_get_similar_other() to get the + * complementary vector (i.e., both vectors will have the same types). + * The caller <b>should not</b> call apol_vector_destroy() upon the + * returned vector. If the user did not request this analysis then + * the return value will be NULL. + * + * @param result Types relation result from which to get similar accesses. + * + * @return Vector of similar accesses, or NULL if analysis was not run. + */ + extern const apol_vector_t *apol_types_relation_result_get_similar_first(const apol_types_relation_result_t * result); + +/** + * Return the vector of accesses similar to the two types. This is a + * vector of apol_types_relation_access_t pointers. The vector will + * contain only the rules that the other type had. Call + * apol_types_relation_result_get_similar_first() to get the + * complementary vector (i.e., both vectors will have the same types). + * The caller <b>should not</b> call apol_vector_destroy() upon the + * returned vector. If the user did not request this analysis then + * the return value will be NULL. + * + * @param result Types relation result from which to get similar accesses. + * + * @return Vector of similar accesses, or NULL if analysis was not run. + */ + extern const apol_vector_t *apol_types_relation_result_get_similar_other(const apol_types_relation_result_t * result); + +/** + * Return the vector of accesses dissimilar for the first type (i.e., + * types that the first type reaches that the other type does not). + * This is a vector of apol_types_relation_access_t pointers. The + * caller <b>should not</b> call apol_vector_destroy() upon the + * returned vector. If the user did not request this analysis then + * the return value will be NULL. + * + * @param result Types relation result from which to get dissimilar + * accesses. + * + * @return Vector of dissimilar accesses, or NULL if analysis was not + * run. + */ + extern const apol_vector_t *apol_types_relation_result_get_dissimilar_first(const apol_types_relation_result_t * result); + +/** + * Return the vector of accesses dissimilar for the other type (i.e., + * types that the other type reaches that the first type does not). + * This is a vector of apol_types_relation_access_t pointers. The + * caller <b>should not</b> call apol_vector_destroy() upon the + * returned vector. If the user did not request this analysis then + * the return value will be NULL. + * + * @param result Types relation result from which to get dissimilar + * accesses. + * + * @return Vector of dissimilar accesses, or NULL if analysis was not + * run. + */ + extern const apol_vector_t *apol_types_relation_result_get_dissimilar_other(const apol_types_relation_result_t * result); + +/** + * Return the vector of allow rules involving both types (allow one + * type to the other). This is a vector of qpol_avrule_t pointers. + * The caller <b>should not</b> call apol_vector_destroy() upon the + * returned vector. If the user did not request this analysis then + * the return value will be NULL. + * + * @param result Types relation result from which to get rules. + * + * @return Vector of allow rules, or NULL if analysis was not run. + */ + extern const apol_vector_t *apol_types_relation_result_get_allowrules(const apol_types_relation_result_t * result); + +/** + * Return the vector of type transition / type change rules involving + * both types. This is a vector of qpol_terule_t pointers. The + * caller <b>should not</b> call apol_vector_destroy() upon the + * returned vector. If the user did not request this analysis then + * the return value will be NULL. + * + * @param result Types relation result from which to get rules. + * + * @return Vector of type enforcement rules, or NULL if analysis was + * not run. + */ + extern const apol_vector_t *apol_types_relation_result_get_typerules(const apol_types_relation_result_t * result); + +/** + * Return the vector of apol_infoflow_result_t pointers corresponding + * to a direct information flow analysis between both types. The + * caller <b>should not</b> call apol_vector_destroy() upon the + * returned vector. If the user did not request this analysis then + * the return value will be NULL. + * + * @param result Types relation result from which to get information + * flows. + * + * @return Vector of infoflow results, or NULL if analysis was not + * run. + */ + extern const apol_vector_t *apol_types_relation_result_get_directflows(const apol_types_relation_result_t * result); + +/** + * Return the vector of apol_infoflow_result_t pointers corresponding + * to a transitive information flow analysis between the first type to + * the other. The caller <b>should not</b> call apol_vector_destroy() + * upon the returned vector. If the user did not request this + * analysis then the return value will be NULL. + * + * @param result Types relation result from which to get information + * flows. + * + * @return Vector of infoflow results, or NULL if analysis was not + * run. + */ + extern const apol_vector_t *apol_types_relation_result_get_transflowsAB(const apol_types_relation_result_t * result); + +/** + * Return the vector of apol_infoflow_result_t pointers corresponding + * to a transitive information flow analysis between the other type to + * the first. The caller <b>should not</b> call apol_vector_destroy() + * upon the returned vector. If the user did not request this + * analysis then the return value will be NULL. + * + * @param result Types relation result from which to get information + * flows. + * + * @return Vector of infoflow results, or NULL if analysis was not + * run. + */ + extern const apol_vector_t *apol_types_relation_result_get_transflowsBA(const apol_types_relation_result_t * result); + +/** + * Return the vector of apol_domain_trans_result_t pointers + * corresponding to a domain transition analysis between the first + * type to the other. The caller <b>should not</b> call + * apol_vector_destroy() upon the returned vector. If the user did + * not request this analysis then the return value will be NULL. + * + * @param result Types relation result from which to get domain + * transitions. + * + * @return Vector of domain transition results, or NULL if analysis + * was not run. + */ + extern const apol_vector_t *apol_types_relation_result_get_domainsAB(const apol_types_relation_result_t * result); + +/** + * Return the vector of apol_domain_trans_result_t pointers + * corresponding to a domain transition analysis between the other + * type to the first. The caller <b>should not</b> call + * apol_vector_destroy() upon the returned vector. If the user did + * not request this analysis then the return value will be NULL. + * + * @param result Types relation result from which to get domain + * transitions. + * + * @return Vector of domain transition results, or NULL if analysis + * was not run. + */ + extern const apol_vector_t *apol_types_relation_result_get_domainsBA(const apol_types_relation_result_t * result); + +/** + * Given a types relation access node, return the type stored within. + * + * @param a Types relation access node. + * + * @return Pointer to the type stored within. + */ + extern const qpol_type_t *apol_types_relation_access_get_type(const apol_types_relation_access_t * a); + +/** + * Given a types relation access node, return the vector of + * qpol_avrule_t pointers stored within. + * + * @param a Types relation access node. + * + * @return Pointer to the vector of rules. The caller <b>must not</b> + * destroy this vector. + */ + extern const apol_vector_t *apol_types_relation_access_get_rules(const apol_types_relation_access_t * a); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/user-query.h b/libapol/include/apol/user-query.h new file mode 100644 index 0000000..9038bf4 --- /dev/null +++ b/libapol/include/apol/user-query.h @@ -0,0 +1,150 @@ +/** + * @file + * Public Interface for querying users of a policy. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_USER_QUERY_H +#define APOL_USER_QUERY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "policy.h" +#include "vector.h" +#include "mls-query.h" +#include <qpol/policy.h> + + typedef struct apol_user_query apol_user_query_t; + +/******************** user queries ********************/ + +/** + * Execute a query against all users within the policy. + * + * @param p Policy within which to look up users. + * @param u Structure containing parameters for query. If this is + * NULL then return all users. + * @param v Reference to a vector of qpol_user_t. The vector will be + * allocated by this function. The caller must call + * apol_vector_destroy() afterwards. This will be set to NULL upon no + * results or upon error. + * + * @return 0 on success (including none found), negative on error. + */ + extern int apol_user_get_by_query(const apol_policy_t * p, apol_user_query_t * u, apol_vector_t ** v); + +/** + * Allocate and return a new user query structure. All fields are + * initialized, such that running this blank query results in + * returning all users within the policy. The caller must call + * apol_user_query_destroy() upon the return value afterwards. + * + * @return An initialized user query structure, or NULL upon error. + */ + extern apol_user_query_t *apol_user_query_create(void); + +/** + * Deallocate all memory associated with the referenced user query, + * and then set it to NULL. This function does nothing if the query + * is already NULL. + * + * @param u Reference to a user query structure to destroy. + */ + extern void apol_user_query_destroy(apol_user_query_t ** u); + +/** + * Set a user query to return only users that match this name. This + * function duplicates the incoming name. + * + * @param p Policy handler, to report errors. + * @param u User query to set. + * @param name Limit query to only users this name, or NULL to unset + * this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_user_query_set_user(const apol_policy_t * p, apol_user_query_t * u, const char *name); + +/** + * Set a user query to return only users containing this role. This + * function duplicates the incoming name. + * + * @param p Policy handler, to report errors. + * @param u User query to set. + * @param role Limit query to only users with this role, or NULL to + * unset this field. + * + * @return 0 on success, negative on error. + */ + extern int apol_user_query_set_role(const apol_policy_t * p, apol_user_query_t * u, const char *role); + +/** + * Set a user query to return only users containing this default + * level. This function takes ownership of the level, such that the + * caller must not modify nor destroy it afterwards. + * + * @param p Policy handler, to report errors. + * @param u User query to which set. + * @param level Limit query to only users with this level as their + * default, or NULL to unset this field. + * + * @return Always returns 0. + */ + extern int apol_user_query_set_default_level(const apol_policy_t * p, apol_user_query_t * u, apol_mls_level_t * level); + +/** + * Set a user query to return only users matching a MLS range. This + * function takes ownership of the range, such that the caller must + * not modify nor destroy it afterwards. + * + * @param p Policy handler, to report errors. + * @param u User query to set. + * @param range Limit query to only users matching this range, or NULL + * to unset this field. + * @param range_match Specifies how to match a user to a range. This + * must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or + * APOL_QUERY_EXACT. This parameter is ignored if range is NULL. + * + * @return Always returns 0. + */ + extern int apol_user_query_set_range(const apol_policy_t * p, apol_user_query_t * u, apol_mls_range_t * range, + unsigned int range_match); + +/** + * Set a user query to use regular expression searching for all of its + * fields. Strings will be treated as regexes instead of literals. + * + * @param p Policy handler, to report errors. + * @param u User query to set. + * @param is_regex Non-zero to enable regex searching, 0 to disable. + * + * @return Always 0. + */ + extern int apol_user_query_set_regex(const apol_policy_t * p, apol_user_query_t * u, int is_regex); + +#ifdef __cplusplus +} +#endif + +#endif /* APOL_USER_QUERY_H */ diff --git a/libapol/include/apol/util.h b/libapol/include/apol/util.h new file mode 100644 index 0000000..99db168 --- /dev/null +++ b/libapol/include/apol/util.h @@ -0,0 +1,336 @@ +/** + * @file + * + * Miscellaneous, uncategorized functions for libapol. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2001-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_UTIL_H +#define APOL_UTIL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "vector.h" + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +/** + * Return an immutable string describing this library's version. + * + * @return String describing this library. + */ + extern const char *libapol_get_version(void); + +/** + * Given a portcon protocol, return a read-only string that describes + * that protocol. + * + * @param protocol Portcon protocol, one of IPPROTO_TCP or IPPROTO_UDP + * from netinet/in.h. + * + * @return A string that describes the protocol, or NULL if the + * protocol is invalid. <b>Do not free() this string.</b> + */ + extern const char *apol_protocol_to_str(uint8_t protocol); + +/** + * Given the name of a portcon protocol, return its numeric value. + * + * @param protocol_str Portcon protocol, one of "tcp", "TCP", "udp", or "UDP". + * + * @return Numeric value for the protocol, one of IPPROTO_TCP or IPPROTO_UDP + * from netinet/in.h. Upon error return 0. + */ + extern uint8_t apol_str_to_protocol(const char *protocol_str); + +/** + * Given a string representing and IP value (mask or address, IPv4 or + * IPv6), write to an array that value in the same bit order that + * qpol uses. If the IP was in IPv4 format, only write to the first + * element and zero the remainder. + * + * @param str A string representing and IP value, either in IPv4 or + * IPv6 format. + * @param ip Array to which write converted value. + * + * @return QPOL_IPV4 if the string is in IPv4 format, QPOL_IPV6 if + * in IPv6, < 0 on error. + */ + extern int apol_str_to_internal_ip(const char *str, uint32_t ip[4]); + +/** + * Given a genfscon object class, return a read-only string that + * describes that class. + * + * @param objclass Object class, one of QPOL_CLASS_BLK_FILE, + * QPOL_CLASS_CHR_FILE, etc. + * + * @return A string that describes the object class, or NULL if the + * object class is invalid. <b>Do not free() this string.</b> + * + * @see <qpol/genfscon_query.h> for a list of valid object classes. + */ + extern const char *apol_objclass_to_str(uint32_t objclass); + +/** + * Given a string representing a genfscon object class, return its + * numeric identifier. Valid strings may be obtained by calling + * apol_objclass_to_str(). + * + * @param objclass Object class, one of "any", "file", etc. + * + * @return Numeric identifier for object class, or 0 if unknown. + * + * @see <qpol/genfscon_query.h> for a list of valid object classes. + */ + extern uint32_t apol_str_to_objclass(const char *objclass); + +/** + * Given a fs_use behavior type, return a read-only string that + * describes that fs_use behavior. + * + * @param behavior A fs_use behavior, one of QPOL_FS_USE_PSID, + * QPOL_FS_USE_XATTR, etc. + * + * @return A string that describes the behavior, or NULL if the + * behavior is invalid. <b>Do not free() this string.</b> + */ + extern const char *apol_fs_use_behavior_to_str(uint32_t behavior); + +/** + * Given a fs_use behavior string, return its numeric value. + * + * @param behavior A fs_use behavior, one of "fs_use_psid", + * "fs_use_xattr", etc. + * + * @return A numeric representation for the behavior, one of + * QPOL_FS_USE_PSID, QPOL_FS_USE_XATTR, etc, or < 0 if the string is + * invalid. + */ + extern int apol_str_to_fs_use_behavior(const char *behavior); + +/** + * Given a rule type, return a read-only string that describes that + * rule. + * + * @param rule_type A policy rule type, one of QPOL_RULE_ALLOW, + * QPOL_RULE_TYPE_CHANGE, etc. + * + * @return A string that describes the rule, or NULL if the rule_type + * is invalid. <b>Do not free() this string.</b> + */ + extern const char *apol_rule_type_to_str(uint32_t rule_type); + +/** + * Given a conditional expression type, return a read-only string that + * describes that operator. + * + * @param expr_type An expression type, one of QPOL_COND_EXPR_BOOL, + * QPOL_COND_EXPR_NOT, etc. + * + * @return A string that describes the expression, or NULL if the + * expr_type is invalid. <b>Do not free() this string.</b> + */ + extern const char *apol_cond_expr_type_to_str(uint32_t expr_type); + +/** + * Given a file name, search and return that file's path on the + * running system. First search the present working directory, then + * the directory at APOL_INSTALL_DIR (an environment variable), then + * apol's install dir. + * + * @param file_name File to find. + * + * @return File's path, or NULL if not found. Caller must free() this + * string afterwards. + */ + extern char *apol_file_find(const char *file_name); + +/** + * Given a file name, search and return that file's full path + * (directory + file name) on the running system. First search the + * present working directory, then the directory at APOL_INSTALL_DIR + * (an environment variable), then apol's install dir. + * + * @param file_name File to find. + * + * @return File's path + file name, or NULL if not found. Caller must + * free() this string afterwards. + */ + extern char *apol_file_find_path(const char *file_name); + +/** + * Given a file name for a user configuration, search and return that + * file's path + file name in the user's home directory. + * + * @param file_name File to find. + * + * @return File's path + file name, or NULL if not found. Caller must + * free() this string afterwards. + */ + extern char *apol_file_find_user_config(const char *file_name); + +/** + * Given a file name, read the file's contents into a newly allocated + * buffer. The caller must free() this buffer afterwards. + * + * @param fname Name of file to read. + * @param buf Reference to a newly allocated buffer. + * @param len Reference to the number of bytes read. + * + * @return 0 on success, < 0 on error. + */ + extern int apol_file_read_to_buffer(const char *fname, char **buf, size_t * len); +/** + * Given a file pointer into a config file, read and return the value + * for the given config var. The caller must free() the returned + * string afterwards. + * + * @param var Name of configuration variable to obtain. + * @param fp An open file pointer into a configuration file. This + * function will not maintain the pointer's current location. + * + * @return A newly allocated string containing the variable's value, + * or NULL if not found or error. + */ + extern char *apol_config_get_var(const char *var, FILE * fp); + +/** + * Given a string of tokens, allocate and return a vector of strings + * initialized to those tokens. + * + * @param s String to split. + * @param delim Delimiter for tokens, as per strsep(3). + * + * @return A newly allocated vector of strings containing the + * variable's values, or NULL if not found or error. Note that the + * vector could be empty if the config var does not exist or has an + * empty value. The caller must call apol_vector_destroy() + * afterwards. + */ + extern apol_vector_t *apol_str_split(const char *s, const char *delim); + +/** + * Given a vector of strings, allocate and return a string that joins + * the vector using the given separator. The caller is responsible + * for free()ing the string afterwards. + * + * @param list Vector of strings to join. + * @param delim Delimiter character(s) for the concatenated string. + * + * @return An allocated concatenated string, or NULL upon error. If + * the list is empty then return an empty string. The caller is + * responsible for calling free() upon the return value. + */ + extern char *apol_str_join(const apol_vector_t * list, const char *delim); + +/** + * Given a mutable string, modify the string by removing both starting + * and trailing whitespace characters. + * + * @param str String to modify. + */ + extern void apol_str_trim(char *str); + +/** + * Append a string to an existing dynamic mutable string, expanding + * the target string if necessary. The caller must free() the target + * string. If tgt is NULL then initially allocate the resulting + * string. + * + * @param tgt Reference to a string to modify, or NULL to create a new + * string. + * @param tgt_sz Pointer to number of bytes currently allocated to + * tgt. This will be updated with the new string size. If *tgt is + * NULL then this existing value is ignored. (It will still be updated + * afterwards). + * @param str String to append. + * + * @return 0 on success. On error, return < 0 and set errno; tgt will be + * free()d and set to NULL, tgt_sz will be set to 0. + */ + extern int apol_str_append(char **tgt, size_t * tgt_sz, const char *str); + +/** + * Append a string to an existing dynamic mutable string, expanding + * the target string if necessary. The string to append is computed + * using the format string, as per printf(3). The caller must free() + * the target string. If tgt is NULL then initially allocate the + * resulting string. + * + * @param tgt Reference to a string to modify, or NULL to create a new + * string. + * @param tgt_sz Pointer to number of bytes currently allocated to + * tgt. This will be updated with the new string size. If *tgt is + * NULL then the existing value is ignored. (It will still be updated + * afterwards). + * @param fmt Format for the string with which append, as per + * printf(3). + * + * @return 0 on success. On error, return < 0 and set errno; tgt will be + * free()d and set to NULL, tgt_sz will be set to 0. + */ + extern int apol_str_appendf(char **tgt, size_t * tgt_sz, const char *fmt, ...); + +/* declaration duplicated below to satisfy doxygen */ + extern int apol_str_appendf(char **tgt, size_t * tgt_sz, const char *fmt, ...) __attribute__ ((format(printf, 3, 4))); + +/** + * Test whether a given string is only white space. + * + * @param str String to test. + * @return 1 if string is either NULL or only whitespace, 0 otherwise. + */ + extern int apol_str_is_only_white_space(const char *str); + +/** + * Wrapper around strcmp for use in vector and BST comparison functions. + * + * @param a String to compare. + * @param b The other string to compare. + * @param unused Not used. (exists to match expected function signature) + * + * @return Less than, equal to, or greater than 0 if string a is found + * to be less than, identical to, or greater than string b + * respectively. + */ + extern int apol_str_strcmp(const void *a, const void *b, void *unused __attribute__ ((unused))); + +/** + * Wrapper around strdup for use in vector and BST cloning functions. + * + * @param elem String to duplicate. + * @param unused Not used. (exists to match expected function signature) + * + * @return A new string that is a duplicate of elem, or NULL upon error. + */ + extern void *apol_str_strdup(const void *elem, void *unused __attribute__ ((unused))); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/include/apol/vector.h b/libapol/include/apol/vector.h new file mode 100644 index 0000000..f690bf0 --- /dev/null +++ b/libapol/include/apol/vector.h @@ -0,0 +1,335 @@ +/** + * @file + * Contains the API for a generic vector. Note that vector functions + * are not thread-safe. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_VECTOR_H +#define APOL_VECTOR_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <stdlib.h> +#include <qpol/iterator.h> + + typedef struct apol_vector apol_vector_t; + + typedef int (apol_vector_comp_func) (const void *a, const void *b, void *data); + typedef void (apol_vector_free_func) (void *elem); + typedef void *(apol_vector_dup_func) (const void *elem, void *data); + +/** + * Allocate and initialize an empty vector with default + * capacity. + * + * @param fr Function to call when destroying the vector. Each + * element of the vector will be passed into this function; it should + * free the memory used by that element. If this parameter is NULL, + * the elements will not be freed. + * + * @return A pointer to a newly created vector on success and NULL on + * failure. If the call fails, errno will be set. The caller is + * responsible for calling apol_vector_destroy() to free memory used. + */ + extern apol_vector_t *apol_vector_create(apol_vector_free_func * fr); + +/** + * Allocate and initialize an empty vector with starting capacity of + * cap. + * + * @param cap The starting capacity to allocate for the internal + * array. + * @param fr Function to call when destroying the vector. Each + * element of the vector will be passed into this function; it should + * free the memory used by that element. If this parameter is NULL, + * the elements will not be freed. + * + * @return A pointer to a newly created vector on success and NULL on + * failure. If the call fails, errno will be set. The caller is + * responsible for calling apol_vector_destroy() to free memory used. + */ + extern apol_vector_t *apol_vector_create_with_capacity(size_t cap, apol_vector_free_func * fr); + +/** + * Allocate and return a vector that has been initialized with the + * contents of a qpol iterator. <b>This function merely makes a + * shallow copy of the iterator's contents</b>; any memory ownership + * restrictions imposed by the iterator apply to this vector as well. + * Also note that this function begins copying from the iterator's + * current position, leaving the iterator at its end position + * afterwards. + * + * @param iter qpol iterator from which to obtain vector's contents. + * @param fr Function to call when destroying the vector. Each + * element of the vector will be passed into this function; it should + * free the memory used by that element. If this parameter is NULL, + * the elements will not be freed. + * + * @return A pointer to a newly created vector on success and NULL on + * failure. If the call fails, errno will be set. The caller is + * responsible for calling apol_vector_destroy() to free memory used. + */ + extern apol_vector_t *apol_vector_create_from_iter(qpol_iterator_t * iter, apol_vector_free_func * fr); + +/** + * Allocate and return a vector that has been initialized with the + * contents of another vector. + * + * @param v Vector from which to copy. + * @param dup If NULL, then make a shallow copy of the original + * vector's contents. Otherwise this function will be called upon + * for each element from the original vector; the return value will + * be the value stored in the new vector. + * @param data Arbitrary data to pass as dup's second parameter. + * @param fr Function to call when destroying the new vector. Each + * element of the vector will be passed into this function; it should + * free the memory used by that element. If this parameter is NULL, + * the elements will not be freed. + * + * @return A pointer to a newly created vector on success and NULL on + * failure. If the call fails, errno will be set. The caller is + * responsible for calling apol_vector_destroy() to free memory used. + */ + extern apol_vector_t *apol_vector_create_from_vector(const apol_vector_t * v, apol_vector_dup_func * dup, void *data, + apol_vector_free_func * fr); + +/** + * Allocate and return a vector that has been initialized with the + * contents common to two other vectors. <b>This function merely + * makes a shallow copy of the vectors' contents</b>; any memory + * ownership restrictions imposed by the original vectors apply to + * this new vector as well. Note that if a source vector contains + * duplicate elements the returned vector may (or may not) have + * duplicates as well. If the caller does not want duplicate entries + * then apol_vector_sort_uniquify() should be called afterwards. + * + * @param v1 First vector from which to compute the intersection. + * @param v2 Other vector to compute intersection. + * @param cmp A comparison call back for the type of element stored + * in the vector. The expected return value from this function is + * less than, equal to, or greater than 0 if the first argument is + * less than, equal to, or greater than the second respectively. If + * this is NULL then do pointer address comparison. + * @param data Arbitrary data to pass as the comparison function's + * third paramater. + * + * @return A pointer to a newly created vector on success and NULL on + * failure. If the call fails, errno will be set. The caller is + * responsible for calling apol_vector_destroy() to free memory used. + */ + extern apol_vector_t *apol_vector_create_from_intersection(const apol_vector_t * v1, + const apol_vector_t * v2, apol_vector_comp_func * cmp, + void *data); + +/** + * Free a vector and any memory used by it. This will recursively + * invoke the free function that was stored within the vector when it + * was created. + * + * @param v Pointer to the vector to free. The pointer will be set + * to NULL afterwards. If already NULL then this function does + * nothing. + */ + extern void apol_vector_destroy(apol_vector_t ** v); + +/** + * Get the number of elements in the vector. + * + * @param v The vector from which to get the number of elements. + * Must be non-NULL. + * + * @return The number of elements in the vector; if v is NULL, + * returns 0. + */ + extern size_t apol_vector_get_size(const apol_vector_t * v); + +/** + * Get the current capacity of the vector. + * + * @param v The vector from which to get the current capacity. Must + * be non-NULL. + * + * @return The capacity of the vector; this value will be greater or + * equal to the number of elements in the vector. If v is NULL, + * returns 0. + */ + extern size_t apol_vector_get_capacity(const apol_vector_t * v); + +/** + * Get the element at the requested index. + * + * @param v The vector from which to get the element. + * @param idx The index of the desired element. + * + * @return A pointer to the element requested. If v is NULL or idx is + * out of range, returns NULL and sets errno. + */ + extern void *apol_vector_get_element(const apol_vector_t * v, size_t idx); + +/** + * Find an element within a vector, returning its index within the vector. + * + * @param v The vector from which to get the element. + * @param elem The element to find. + * @param cmp A comparison call back for the type of element stored + * in the vector. The first parameter will be an existing element + * from the vector; next will be elem and then data. The expected + * return value from this function is less than, equal to, or greater + * than 0 if the first argument is less than, equal to, or greater + * than the second respectively. For use in this function the return + * value is only checked for 0 or non-zero return. If this is NULL + * then do pointer address comparison. + * @param data Arbitrary data to pass as the comparison function's + * third paramater. + * @param i Index into vector where element was found. This value is + * undefined if the element was not found. + * + * @return 0 if element was found, or < 0 if not found. + */ + extern int apol_vector_get_index(const apol_vector_t * v, const void *elem, apol_vector_comp_func * cmp, void *data, + size_t * i); + +/** + * Add an element to the end of a vector. + * + * @param v The vector to which to add the element. + * @param elem The element to add. Once added the element will be + * the last element in the vector. + * + * @return 0 on success and < 0 on failure. If the call fails, errno + * will be set and v will be unchanged. + */ + extern int apol_vector_append(apol_vector_t * v, void *elem); + +/** + * Add an element to the end of a vector unless that element is equal + * to an existing element. + * + * @param v The vector to which to add the element. + * @param elem The element to add; must be non-NULL. + * @param cmp A comparison call back for the type of element stored + * in the vector. The expected return value from this function is + * less than, equal to, or greater than 0 if the first argument is + * less than, equal to, or greater than the second respectively. For + * use in this function the return value is only checked for 0 or + * non-zero return. If this is NULL then do pointer address + * comparison. + * @param data Arbitrary data to pass as the comparison function's + * third paramater. + * + * @return 0 on success, < 0 on failure, and > 0 if the element + * already exists in the vector. If the call fails or the element + * already exists errno will be set. + */ + extern int apol_vector_append_unique(apol_vector_t * v, void *elem, apol_vector_comp_func * cmp, void *data); + +/** + * Concatenate two vectors. Appends all elements of src to dest. + * <b>NOTE: No type checking is done for elements in the two + * vectors.</b> Elements are not deep copies. + * @param dest Vector to which to append elements. + * @param src Vector containing elements to append. + * @return 0 on success and < 0 on failure; if the call fails, + * errno will be set and dest's contents will be reverted. + */ + extern int apol_vector_cat(apol_vector_t * dest, const apol_vector_t * src); + +/** + * Remove an element from a vector, and renumber all subsequent + * elements. <b>This does not free memory that was used by the + * removed element</b>; the caller is responsible for doing that. + * + * @param v Vector containing element. + * @param idx Index to the element to remove. + * @return 0 on success and < 0 on failure; if the call fails, + * errno will be set and v's contents will be reverted. + */ + extern int apol_vector_remove(apol_vector_t * v, const size_t idx); + +/** + * Compare two vectors, determining if one is different than the + * other. This uses a callback to compare elements across the + * vectors. + * + * @param a First vector to compare. + * @param b Second vector to compare. + * @param cmp A comparison call back for the type of element stored + * in the vector. The expected return value from this function is + * less than, equal to, or greater than 0 if the first argument is + * less than, equal to, or greater than the second respectively. If + * this is NULL then do pointer address comparison. + * @param data Arbitrary data to pass as the comparison function's + * third paramater. + * @param i Reference to where to store the index of the first + * detected difference. The value is undefined if vectors are + * equivalent (return value of 0). Note that the index may be + * greater than a vector's size if the vectors are of unequal + * lengths. + * + * @return < 0 if vector A is less than B, > 0 if A is greater than + * B, or 0 if equivalent. + */ + extern int apol_vector_compare(const apol_vector_t * a, const apol_vector_t * b, apol_vector_comp_func * cmp, void *data, + size_t * i); + +/** + * Sort the vector's elements within place, using an unstable sorting + * algorithm. + * + * @param v The vector to sort. + * @param cmp A comparison call back for the type of element stored + * in the vector. The expected return value from this function is + * less than, equal to, or greater than 0 if the first argument is + * less than, equal to, or greater than the second respectively. If + * this is NULL then treat the vector's contents as unsigned integers + * and sort in increasing order. + * @param data Arbitrary data to pass as the comparison function's + * third paramater. + */ + extern void apol_vector_sort(apol_vector_t * v, apol_vector_comp_func * cmp, void *data); + +/** + * Sort the vector's elements within place (see apol_vector_sort()), + * and then compact vector by removing duplicate entries. The + * vector's free function will be used to free the memory used by + * non-unique elements. + * + * @param v The vector to sort. + * @param cmp A comparison call back for the type of element stored + * in the vector. The expected return value from this function is + * less than, equal to, or greater than 0 if the first argument is + * less than, equal to, or greater than the second respectively. If + * this is NULL then treat the vector's contents as unsigned integers + * and sort in increasing order. + * @param data Arbitrary data to pass as the comparison function's + * third paramater. + */ + extern void apol_vector_sort_uniquify(apol_vector_t * v, apol_vector_comp_func * cmp, void *data); + +#ifdef __cplusplus +} +#endif + +#endif /* APOL_VECTOR_H */ diff --git a/libapol/src/Makefile.am b/libapol/src/Makefile.am new file mode 100644 index 0000000..3fa4f06 --- /dev/null +++ b/libapol/src/Makefile.am @@ -0,0 +1,75 @@ +lib_LIBRARIES = libapol.a +setoolsdir = @setoolsdir@ + +apolso_DATA = libapol.so.@libapol_version@ +apolsodir = $(libdir) + +AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \ + @QPOL_CFLAGS@ -fpic -I$(srcdir)/../include \ + -DAPOL_INSTALL_DIR='"${setoolsdir}"' \ + -DLIBAPOL_POLICY_INSTALL_DIR='"@selinux_policy_dir@/policy"' \ + -DLIBAPOL_DEFAULT_POLICY='"@selinux_default_policy@"' +AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ + +libapol_a_SOURCES = \ + avrule-query.c \ + bool-query.c \ + bst.c \ + class-perm-query.c \ + condrule-query.c \ + constraint-query.c \ + context-query.c \ + domain-trans-analysis.c domain-trans-analysis-internal.h \ + fscon-query.c \ + infoflow-analysis.c infoflow-analysis-internal.h \ + isid-query.c \ + mls-query.c \ + mls_level.c \ + mls_range.c \ + netcon-query.c \ + perm-map.c \ + permissive-query.c \ + polcap-query.c \ + policy.c \ + policy-path.c \ + policy-query.c \ + queue.c \ + range_trans-query.c \ + rbacrule-query.c \ + relabel-analysis.c \ + render.c \ + role-query.c \ + terule-query.c \ + type-query.c \ + types-relation-analysis.c \ + user-query.c \ + util.c \ + vector.c vector-internal.h \ + policy-query-internal.h queue.h + +libapol_a_DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so + +libapol_so_OBJS = $(patsubst %.c,%.o,$(filter %.c,$(libapol_a_SOURCES))) +LIBAPOL_SONAME = @libapol_soname@ + +dist_noinst_DATA = libapol.map + +$(apolso_DATA): $(libapol_so_OBJS) libapol.map + $(CC) -shared -o $@ $(libapol_so_OBJS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(LIBAPOL_SONAME),--version-script=$(srcdir)/libapol.map,-z,defs $(top_builddir)/libqpol/src/libqpol.so + $(LN_S) -f $@ @libapol_soname@ + $(LN_S) -f $@ libapol.so + +libapol.so: $(apolso_DATA) + +$(top_builddir)/libqpol/src/libqpol.so: + $(MAKE) -C $(top_builddir)/libqpol/src $(notdir $@) + +install-data-hook: + cd $(DESTDIR)$(apolsodir) && $(LN_S) -f $(apolso_DATA) @libapol_soname@ + cd $(DESTDIR)$(apolsodir) && $(LN_S) -f $(apolso_DATA) libapol.so + +mostlyclean-local: + -rm -rf *.gcno *.gcda *.gprof *.gcov libapol.so @libapol_soname@ $(apolso_DATA) + +uninstall-local: + -rm -rf $(DESTDIR)$(apolsodir)/$(apolso_DATA) $(DESTDIR)$(apolsodir)/@libapol_soname@ $(DESTDIR)$(apolsodir)/libapol.so diff --git a/libapol/src/avrule-query.c b/libapol/src/avrule-query.c new file mode 100644 index 0000000..c409e30 --- /dev/null +++ b/libapol/src/avrule-query.c @@ -0,0 +1,1209 @@ +/** + * @file + * + * Provides a way for setools to make queries about access vector + * rules within a policy. The caller obtains a query object, fills in + * its parameters, and then runs the query; it obtains a vector of + * results. Searches are conjunctive -- all fields of the search + * query must match for a datum to be added to the results query. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2008 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" +#include <apol/bst.h> +#include <qpol/policy_extend.h> +#include <errno.h> +#include <string.h> + +struct apol_avrule_query +{ + char *source, *target, *bool_name; + apol_vector_t *classes, *perms; + unsigned int rules; + unsigned int flags; +}; + +/** + * Common semantic rule selection routine used in get*rule_by_query. + * @param p Policy to search. + * @param v Vector of rules to populate (of type qpol_avrule_t). + * @param rule_type Mask of rules to search. + * @param flags Query options as specified by the apol_avrule_query. + * @param source_list If non-NULL, list of types to use as source. + * If NULL, accept all types. + * @param target_list If non-NULL, list of types to use as target. + * If NULL, accept all types. + * @param class_list If non-NULL, list of classes to use. + * If NULL, accept all classes. + * @param perm_list If non-NULL, list of permisions to use. + * If NULL, accept all permissions. + * @param bool_name If non-NULL, find conditional rules affected by this boolean. + * If NULL, all rules will be considered (including unconditional rules). + * @return 0 on success and < 0 on failure. + */ +static int rule_select(const apol_policy_t * p, apol_vector_t * v, uint32_t rule_type, unsigned int flags, + const apol_vector_t * source_list, const apol_vector_t * target_list, const apol_vector_t * class_list, + const apol_vector_t * perm_list, const char *bool_name) +{ + qpol_iterator_t *iter = NULL, *perm_iter = NULL; + const int only_enabled = flags & APOL_QUERY_ONLY_ENABLED; + const int is_regex = flags & APOL_QUERY_REGEX; + const int source_as_any = flags & APOL_QUERY_SOURCE_AS_ANY; + size_t num_perms_to_match = 1; + int retv = -1; + regex_t *bool_regex = NULL; + + if ((flags & APOL_QUERY_MATCH_ALL_PERMS) && perm_list != NULL) { + num_perms_to_match = apol_vector_get_size(perm_list); + } + if (qpol_policy_get_avrule_iter(p->p, rule_type, &iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_avrule_t *rule; + uint32_t is_enabled; + const qpol_cond_t *cond = NULL; + int match_source = 0, match_target = 0, match_bool = 0; + size_t match_perm = 0, i; + if (qpol_iterator_get_item(iter, (void **)&rule) < 0) { + goto cleanup; + } + + if (qpol_avrule_get_is_enabled(p->p, rule, &is_enabled) < 0) { + goto cleanup; + } + if (!is_enabled && only_enabled) { + continue; + } + + if (bool_name != NULL) { + if (qpol_avrule_get_cond(p->p, rule, &cond) < 0) { + goto cleanup; + } + if (cond == NULL) { + continue; /* skip unconditional rule */ + } + match_bool = apol_compare_cond_expr(p, cond, bool_name, is_regex, &bool_regex); + if (match_bool < 0) { + goto cleanup; + } else if (match_bool == 0) { + continue; + } + } + + if (source_list == NULL) { + match_source = 1; + } else { + const qpol_type_t *source_type; + if (qpol_avrule_get_source_type(p->p, rule, &source_type) < 0) { + goto cleanup; + } + if (apol_vector_get_index(source_list, source_type, NULL, NULL, &i) == 0) { + match_source = 1; + } + } + + /* if source did not match, but treating source symbol + * as any field, then delay rejecting this rule until + * the target has been checked */ + if (!source_as_any && !match_source) { + continue; + } + + if (target_list == NULL || (source_as_any && match_source)) { + match_target = 1; + } else { + const qpol_type_t *target_type; + if (qpol_avrule_get_target_type(p->p, rule, &target_type) < 0) { + goto cleanup; + } + if (apol_vector_get_index(target_list, target_type, NULL, NULL, &i) == 0) { + match_target = 1; + } + } + + if (!match_target) { + continue; + } + + if (class_list != NULL) { + const qpol_class_t *obj_class; + if (qpol_avrule_get_object_class(p->p, rule, &obj_class) < 0) { + goto cleanup; + } + if (apol_vector_get_index(class_list, obj_class, NULL, NULL, &i) < 0) { + continue; + } + } + + if (perm_list != NULL) { + for (i = 0; i < apol_vector_get_size(perm_list) && match_perm < num_perms_to_match; i++) { + char *perm = (char *)apol_vector_get_element(perm_list, i); + if (qpol_avrule_get_perm_iter(p->p, rule, &perm_iter) < 0) { + goto cleanup; + } + int match = apol_compare_iter(p, perm_iter, perm, 0, NULL, 1); + if (match < 0) { + goto cleanup; + } else if (match > 0) { + match_perm++; + } + qpol_iterator_destroy(&perm_iter); + } + } else { + match_perm = num_perms_to_match; + } + if (match_perm < num_perms_to_match) { + continue; + } + + if (apol_vector_append(v, rule)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retv = 0; + cleanup: + apol_regex_destroy(&bool_regex); + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&perm_iter); + return retv; +} + +int apol_avrule_get_by_query(const apol_policy_t * p, const apol_avrule_query_t * a, apol_vector_t ** v) +{ + apol_vector_t *source_list = NULL, *target_list = NULL, *class_list = NULL, *perm_list = NULL; + int retval = -1, source_as_any = 0, is_regex = 0; + char *bool_name = NULL; + *v = NULL; + unsigned int flags = 0; + + uint32_t rule_type = QPOL_RULE_ALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT; +// if (qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_NEVERALLOW)) { + rule_type |= QPOL_RULE_NEVERALLOW; +// } + if (a != NULL) { + if (a->rules != 0) { + rule_type &= a->rules; + } + flags = a->flags; + is_regex = a->flags & APOL_QUERY_REGEX; + bool_name = a->bool_name; + if (a->source != NULL && + (source_list = + apol_query_create_candidate_type_list(p, a->source, is_regex, + a->flags & APOL_QUERY_SOURCE_INDIRECT, + ((a->flags & (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE)) / + APOL_QUERY_SOURCE_TYPE))) == NULL) { + goto cleanup; + } + if ((a->flags & APOL_QUERY_SOURCE_AS_ANY) && a->source != NULL) { + target_list = source_list; + source_as_any = 1; + } else if (a->target != NULL && + (target_list = + apol_query_create_candidate_type_list(p, a->target, is_regex, + a->flags & APOL_QUERY_TARGET_INDIRECT, + ((a-> + flags & (APOL_QUERY_TARGET_TYPE | APOL_QUERY_TARGET_ATTRIBUTE)) + / APOL_QUERY_TARGET_TYPE))) == NULL) { + goto cleanup; + } + if (a->classes != NULL && + apol_vector_get_size(a->classes) > 0 && + (class_list = apol_query_create_candidate_class_list(p, a->classes)) == NULL) { + goto cleanup; + } + if (a->perms != NULL && apol_vector_get_size(a->perms) > 0) { + perm_list = a->perms; + } + } + + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + + if (rule_select(p, *v, rule_type, flags, source_list, target_list, class_list, perm_list, bool_name)) { + goto cleanup; + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + apol_vector_destroy(&source_list); + if (!source_as_any) { + apol_vector_destroy(&target_list); + } + apol_vector_destroy(&class_list); + /* don't destroy perm_list - it points to query's permission list */ + return retval; +} + +int apol_syn_avrule_get_by_query(const apol_policy_t * p, const apol_avrule_query_t * a, apol_vector_t ** v) +{ + qpol_iterator_t *iter = NULL, *perm_iter = NULL; + apol_vector_t *source_list = NULL, *target_list = NULL, *class_list = NULL, *perm_list = NULL, *syn_v = NULL; + apol_vector_t *target_types_list = NULL; + int retval = -1, source_as_any = 0, is_regex = 0; + char *bool_name = NULL; + regex_t *bool_regex = NULL; + *v = NULL; + size_t i; + unsigned int flags = 0; + + if (!p || !qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_SYN_RULES)) { + ERR(p, "%s", strerror(EINVAL)); + goto cleanup; + } + + uint32_t rule_type = QPOL_RULE_ALLOW | QPOL_RULE_NEVERALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT; + if (a != NULL) { + if (a->rules != 0) { + rule_type &= a->rules; + } + flags = a->flags; + is_regex = a->flags & APOL_QUERY_REGEX; + bool_name = a->bool_name; + if (a->source != NULL && + (source_list = + apol_query_create_candidate_syn_type_list(p, a->source, is_regex, + a->flags & APOL_QUERY_SOURCE_INDIRECT, + ((a->flags & (APOL_QUERY_SOURCE_TYPE | + APOL_QUERY_SOURCE_ATTRIBUTE)) / + APOL_QUERY_SOURCE_TYPE))) == NULL) { + goto cleanup; + } + if ((a->flags & APOL_QUERY_SOURCE_AS_ANY) && a->source != NULL) { + target_list = source_list; + source_as_any = 1; + } else if (a->target != NULL && + (target_list = + apol_query_create_candidate_syn_type_list(p, a->target, is_regex, + a->flags & APOL_QUERY_TARGET_INDIRECT, + ((a->flags & (APOL_QUERY_TARGET_TYPE | + APOL_QUERY_TARGET_ATTRIBUTE)) + / APOL_QUERY_TARGET_TYPE))) == NULL) { + goto cleanup; + } + if (a->classes != NULL && + apol_vector_get_size(a->classes) > 0 && + (class_list = apol_query_create_candidate_class_list(p, a->classes)) == NULL) { + goto cleanup; + } + if (a->perms != NULL && apol_vector_get_size(a->perms) > 0) { + perm_list = a->perms; + } + } + + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + + if (rule_select(p, *v, rule_type, flags, source_list, target_list, class_list, perm_list, bool_name)) { + goto cleanup; + } + + syn_v = apol_avrule_list_to_syn_avrules(p, *v, perm_list); + if (!syn_v) { + goto cleanup; + } + apol_vector_destroy(v); + *v = syn_v; + syn_v = NULL; + + /* if both fields are indirect skip post filtering type sets */ + if ((a->flags & APOL_QUERY_SOURCE_INDIRECT) && (a->flags & (APOL_QUERY_TARGET_INDIRECT | APOL_QUERY_SOURCE_AS_ANY))) { + retval = 0; + goto cleanup; + } + /* if not searching by source or target we are done */ + if (!source_list && !target_list) { + retval = 0; + goto cleanup; + } + + if (source_list && !(a->flags & APOL_QUERY_SOURCE_INDIRECT)) { + apol_vector_destroy(&source_list); + source_list = + apol_query_create_candidate_type_list(p, a->source, is_regex, 0, + ((a->flags & (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE)) / + APOL_QUERY_SOURCE_TYPE)); + if (!source_list) + goto cleanup; + } + if (target_list && (source_as_any || !(a->flags & APOL_QUERY_TARGET_INDIRECT))) { + if (source_as_any) { + target_list = source_list; + } else { + apol_vector_destroy(&target_list); + target_list = + apol_query_create_candidate_type_list(p, a->target, is_regex, 0, + ((a->flags & (APOL_QUERY_SOURCE_TYPE | + APOL_QUERY_SOURCE_ATTRIBUTE)) / + APOL_QUERY_SOURCE_TYPE)); + if (!target_list) + goto cleanup; + } + } + if (target_list) { + target_types_list = apol_vector_create_from_vector(target_list, NULL, NULL, NULL); + if (!target_types_list) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + qpol_type_t *type = NULL; + for (i = 0; i < apol_vector_get_size(target_types_list); i++) { + type = apol_vector_get_element(target_types_list, i); + unsigned char isattr = 0; + qpol_type_get_isattr(p->p, type, &isattr); + if (isattr) { + apol_vector_remove(target_types_list, i); + i--; + } + } + } + for (i = 0; i < apol_vector_get_size(*v); i++) { + qpol_syn_avrule_t *srule = apol_vector_get_element(*v, i); + const qpol_type_set_t *stypes = NULL, *ttypes = NULL; + int uses_source = 0, uses_target = 0; + uint32_t is_self = 0; + qpol_syn_avrule_get_source_type_set(p->p, srule, &stypes); + qpol_syn_avrule_get_target_type_set(p->p, srule, &ttypes); + qpol_syn_avrule_get_is_target_self(p->p, srule, &is_self); + if (source_list && !(a->flags & APOL_QUERY_SOURCE_INDIRECT)) { + uses_source = apol_query_type_set_uses_types_directly(p, stypes, source_list); + if (uses_source < 0) + goto cleanup; + } else if (source_list && a->flags & APOL_QUERY_SOURCE_INDIRECT) { + uses_source = 1; + } else if (!source_list) { + uses_source = 1; + } + + if (target_list + && !((a->flags & APOL_QUERY_TARGET_INDIRECT) || (source_as_any && a->flags & APOL_QUERY_SOURCE_INDIRECT))) { + uses_target = apol_query_type_set_uses_types_directly(p, ttypes, target_list); + if (uses_target < 0) + goto cleanup; + if (is_self) { + uses_target |= apol_query_type_set_uses_types_directly(p, stypes, target_types_list); + if (uses_target < 0) + goto cleanup; + } + } else if (target_list && ((a->flags & APOL_QUERY_TARGET_INDIRECT) + || (source_as_any && a->flags & APOL_QUERY_SOURCE_INDIRECT))) { + uses_target = 1; + } else if (!target_list) { + uses_target = 1; + } + + if (!((uses_source && uses_target) || (source_as_any && (uses_source || uses_target)))) { + apol_vector_remove(*v, i); + i--; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + apol_vector_destroy(&syn_v); + apol_vector_destroy(&source_list); + apol_vector_destroy(&target_types_list); + if (!source_as_any) { + apol_vector_destroy(&target_list); + } + apol_vector_destroy(&class_list); + /* don't destroy perm_list - it points to query's permission list */ + apol_regex_destroy(&bool_regex); + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&perm_iter); + return retval; +} + +apol_avrule_query_t *apol_avrule_query_create(void) +{ + apol_avrule_query_t *a = calloc(1, sizeof(apol_avrule_query_t)); + if (a != NULL) { + a->rules = ~0U; + a->flags = + (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE | APOL_QUERY_TARGET_TYPE | + APOL_QUERY_TARGET_ATTRIBUTE); + } + return a; +} + +void apol_avrule_query_destroy(apol_avrule_query_t ** a) +{ + if (*a != NULL) { + free((*a)->source); + free((*a)->target); + free((*a)->bool_name); + apol_vector_destroy(&(*a)->classes); + apol_vector_destroy(&(*a)->perms); + free(*a); + *a = NULL; + } +} + +int apol_avrule_query_set_rules(const apol_policy_t * p __attribute__ ((unused)), apol_avrule_query_t * a, unsigned int rules) +{ + if (rules != 0) { + a->rules = rules; + } else { + a->rules = ~0U; + } + return 0; +} + +int apol_avrule_query_set_source(const apol_policy_t * p, apol_avrule_query_t * a, const char *symbol, int is_indirect) +{ + apol_query_set_flag(p, &a->flags, is_indirect, APOL_QUERY_SOURCE_INDIRECT); + return apol_query_set(p, &a->source, NULL, symbol); +} + +int apol_avrule_query_set_source_component(const apol_policy_t * p, apol_avrule_query_t * a, unsigned int component) +{ + if (!a || !(component & APOL_QUERY_SYMBOL_IS_BOTH)) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + apol_query_set_flag(p, &a->flags, component & APOL_QUERY_SYMBOL_IS_TYPE, APOL_QUERY_SOURCE_TYPE); + apol_query_set_flag(p, &a->flags, component & APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_SOURCE_ATTRIBUTE); + return 0; +} + +int apol_avrule_query_set_target(const apol_policy_t * p, apol_avrule_query_t * a, const char *symbol, int is_indirect) +{ + apol_query_set_flag(p, &a->flags, is_indirect, APOL_QUERY_TARGET_INDIRECT); + return apol_query_set(p, &a->target, NULL, symbol); +} + +int apol_avrule_query_set_target_component(const apol_policy_t * p, apol_avrule_query_t * a, unsigned int component) +{ + if (!a || !(component && APOL_QUERY_SYMBOL_IS_BOTH)) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + apol_query_set_flag(p, &a->flags, component & APOL_QUERY_SYMBOL_IS_TYPE, APOL_QUERY_TARGET_TYPE); + apol_query_set_flag(p, &a->flags, component & APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_TARGET_ATTRIBUTE); + return 0; +} + +int apol_avrule_query_append_class(const apol_policy_t * p, apol_avrule_query_t * a, const char *obj_class) +{ + char *s = NULL; + if (obj_class == NULL) { + apol_vector_destroy(&a->classes); + } else if ((s = strdup(obj_class)) == NULL || (a->classes == NULL && (a->classes = apol_vector_create(free)) == NULL) + || apol_vector_append(a->classes, s) < 0) { + ERR(p, "%s", strerror(errno)); + free(s); + return -1; + } + return 0; +} + +int apol_avrule_query_append_perm(const apol_policy_t * p, apol_avrule_query_t * a, const char *perm) +{ + char *s; + if (perm == NULL) { + apol_vector_destroy(&a->perms); + } else if ((s = strdup(perm)) == NULL || + (a->perms == NULL && (a->perms = apol_vector_create(free)) == NULL) || apol_vector_append(a->perms, s) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + return -1; + } + return 0; +} + +int apol_avrule_query_set_bool(const apol_policy_t * p, apol_avrule_query_t * a, const char *bool_name) +{ + return apol_query_set(p, &a->bool_name, NULL, bool_name); +} + +int apol_avrule_query_set_enabled(const apol_policy_t * p, apol_avrule_query_t * a, int is_enabled) +{ + return apol_query_set_flag(p, &a->flags, is_enabled, APOL_QUERY_ONLY_ENABLED); +} + +int apol_avrule_query_set_all_perms(const apol_policy_t * p, apol_avrule_query_t * a, int match_all) +{ + return apol_query_set_flag(p, &a->flags, match_all, APOL_QUERY_MATCH_ALL_PERMS); +} + +int apol_avrule_query_set_source_any(const apol_policy_t * p, apol_avrule_query_t * a, int is_any) +{ + return apol_query_set_flag(p, &a->flags, is_any, APOL_QUERY_SOURCE_AS_ANY); +} + +int apol_avrule_query_set_regex(const apol_policy_t * p, apol_avrule_query_t * a, int is_regex) +{ + return apol_query_set_regex(p, &a->flags, is_regex); +} + +/** + * Comparison function for two syntactic avrules. Will return -1 if + * a's line number is before b's, 1 if b is greater. + */ +static int apol_syn_avrule_comp(const void *a, const void *b, void *data) +{ + qpol_syn_avrule_t *r1 = (qpol_syn_avrule_t *) a; + qpol_syn_avrule_t *r2 = (qpol_syn_avrule_t *) b; + apol_policy_t *p = (apol_policy_t *) data; + unsigned long num1, num2; + if (qpol_syn_avrule_get_lineno(p->p, r1, &num1) < 0 || qpol_syn_avrule_get_lineno(p->p, r2, &num2) < 0) { + return 0; + } + if (num1 != num2) { + return (int)num1 - (int)num2; + } + return (int)((char *)r1 - (char *)r2); +} + +apol_vector_t *apol_avrule_to_syn_avrules(const apol_policy_t * p, const qpol_avrule_t * rule, const apol_vector_t * perms) +{ + apol_vector_t *v = NULL; + qpol_iterator_t *iter = NULL, *perm_iter = NULL; + qpol_syn_avrule_t *syn_avrule; + char *perm; + size_t i; + int retval = -1, error = 0, found_perm = 0; + if (qpol_avrule_get_syn_avrule_iter(p->p, rule, &iter) < 0) { + error = errno; + goto cleanup; + } + if ((v = apol_vector_create(NULL)) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&syn_avrule) < 0) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + found_perm = 0; + if (perms != NULL && apol_vector_get_size(perms) > 0) { + if (qpol_syn_avrule_get_perm_iter(p->p, syn_avrule, &perm_iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) { + if (qpol_iterator_get_item(perm_iter, (void **)&perm) < 0) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + if (apol_vector_get_index(perms, perm, apol_str_strcmp, NULL, &i) == 0) { + found_perm = 1; + break; + } + } + } else { + found_perm = 1; + } + if (found_perm && apol_vector_append(v, syn_avrule) < 0) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + } + /* explicit cast to void* since vector's arbitrary data is non-const */ + apol_vector_sort_uniquify(v, apol_syn_avrule_comp, (void *)p); + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&perm_iter); + if (retval != 0) { + apol_vector_destroy(&v); + errno = error; + return NULL; + } + return v; +} + +apol_vector_t *apol_avrule_list_to_syn_avrules(const apol_policy_t * p, const apol_vector_t * rules, const apol_vector_t * perms) +{ + apol_bst_t *b = NULL; + qpol_avrule_t *rule; + qpol_iterator_t *iter = NULL; + qpol_syn_avrule_t *syn_avrule; + char *perm; + apol_vector_t *tmp_v = NULL, *v = NULL; + size_t i, x; + int retval = -1, error = 0, found_perm = 0; + + if ((b = apol_bst_create(apol_syn_avrule_comp, NULL)) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(rules); i++) { + rule = apol_vector_get_element(rules, i); + if (qpol_avrule_get_syn_avrule_iter(p->p, rule, &iter) < 0) { + error = errno; + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&syn_avrule) < 0) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + /* explicit cast to void* since bst's arbitrary data is non-const */ + if (apol_bst_insert(b, syn_avrule, (void *)p) < 0) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + } + qpol_iterator_destroy(&iter); + } + if ((tmp_v = apol_bst_get_vector(b, 1)) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + if (perms == NULL || apol_vector_get_size(perms) == 0) { + v = tmp_v; + tmp_v = NULL; + } else { + if ((v = apol_vector_create(NULL)) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(tmp_v); i++) { + syn_avrule = apol_vector_get_element(tmp_v, i); + found_perm = 0; + if (qpol_syn_avrule_get_perm_iter(p->p, syn_avrule, &iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&perm) < 0) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + if (apol_vector_get_index(perms, perm, apol_str_strcmp, NULL, &x) == 0) { + found_perm = 1; + break; + } + } + qpol_iterator_destroy(&iter); + if (found_perm && apol_vector_append(v, syn_avrule) < 0) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + } + } + retval = 0; + cleanup: + apol_bst_destroy(&b); + qpol_iterator_destroy(&iter); + apol_vector_destroy(&tmp_v); + if (retval != 0) { + apol_vector_destroy(&v); + errno = error; + return NULL; + } + return v; +} + +char *apol_avrule_render(const apol_policy_t * policy, const qpol_avrule_t * rule) +{ + char *tmp = NULL; + const char *rule_type_str, *tmp_name = NULL; + int error = 0; + uint32_t rule_type = 0; + const qpol_type_t *type = NULL; + const qpol_class_t *obj_class = NULL; + qpol_iterator_t *iter = NULL; + size_t tmp_sz = 0, num_perms = 0; + + if (!policy || !rule) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + + /* rule type */ + if (qpol_avrule_get_rule_type(policy->p, rule, &rule_type)) { + return NULL; + } + if (!(rule_type &= (QPOL_RULE_ALLOW | QPOL_RULE_NEVERALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT))) { + ERR(policy, "%s", "Invalid AV rule type"); + errno = EINVAL; + return NULL; + } + if (!(rule_type_str = apol_rule_type_to_str(rule_type))) { + ERR(policy, "%s", "Could not get AV rule type's string"); + errno = EINVAL; + return NULL; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", rule_type_str)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + /* source type */ + if (qpol_avrule_get_source_type(policy->p, rule, &type)) { + error = errno; + goto err; + } + if (qpol_type_get_name(policy->p, type, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + /* target type */ + if (qpol_avrule_get_target_type(policy->p, rule, &type)) { + error = errno; + goto err; + } + if (qpol_type_get_name(policy->p, type, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s : ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + /* object class */ + if (qpol_avrule_get_object_class(policy->p, rule, &obj_class)) { + error = errno; + goto err; + } + if (qpol_class_get_name(policy->p, obj_class, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + /* perms */ + if (qpol_avrule_get_perm_iter(policy->p, rule, &iter)) { + error = errno; + goto err; + } + if (qpol_iterator_get_size(iter, &num_perms)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (num_perms > 1) { + if (apol_str_append(&tmp, &tmp_sz, "{ ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + char *perm_name = NULL; + if (qpol_iterator_get_item(iter, (void **)&perm_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", perm_name)) { + error = errno; + free(perm_name); + ERR(policy, "%s", strerror(error)); + goto err; + } + free(perm_name); + tmp_name = NULL; + } + if (num_perms > 1) { + if (apol_str_append(&tmp, &tmp_sz, "} ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + + if (apol_str_append(&tmp, &tmp_sz, ";")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + qpol_iterator_destroy(&iter); + return tmp; + + err: + free(tmp); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; +} + +char *apol_syn_avrule_render(const apol_policy_t * policy, const qpol_syn_avrule_t * rule) +{ + char *tmp = NULL; + const char *rule_type_str, *tmp_name = NULL; + int error = 0; + uint32_t rule_type = 0, star = 0, comp = 0, self = 0; + const qpol_type_t *type = NULL; + const qpol_class_t *obj_class = NULL; + qpol_iterator_t *iter = NULL, *iter2 = NULL; + size_t tmp_sz = 0, iter_sz = 0, iter2_sz = 0; + const qpol_type_set_t *set = NULL; + + if (!policy || !rule) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + + /* rule type */ + if (qpol_syn_avrule_get_rule_type(policy->p, rule, &rule_type)) { + return NULL; + } + if (!(rule_type &= (QPOL_RULE_ALLOW | QPOL_RULE_NEVERALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT))) { + ERR(policy, "%s", "Invalid AV rule type"); + errno = EINVAL; + return NULL; + } + if (!(rule_type_str = apol_rule_type_to_str(rule_type))) { + ERR(policy, "%s", "Could not get AV rule type's string"); + errno = EINVAL; + return NULL; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", rule_type_str)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + /* source type set */ + if (qpol_syn_avrule_get_source_type_set(policy->p, rule, &set)) { + error = errno; + goto err; + } + if (qpol_type_set_get_is_star(policy->p, set, &star)) { + error = errno; + goto err; + } + if (star) { + if (apol_str_append(&tmp, &tmp_sz, "* ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } else { + if (qpol_type_set_get_is_comp(policy->p, set, &comp)) { + error = errno; + goto err; + } + if (comp) { + if (apol_str_append(&tmp, &tmp_sz, "~")) { + error = errno; + ERR(policy, "%s", strerror(ENOMEM)); + goto err; + } + } + if (qpol_type_set_get_included_types_iter(policy->p, set, &iter)) { + error = errno; + goto err; + } + if (qpol_type_set_get_subtracted_types_iter(policy->p, set, &iter2)) { + error = errno; + goto err; + } + if (qpol_iterator_get_size(iter, &iter_sz) || qpol_iterator_get_size(iter2, &iter2_sz)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (iter_sz + iter2_sz > 1) { + if (apol_str_append(&tmp, &tmp_sz, "{ ")) { + error = errno; + ERR(policy, "%s", strerror(ENOMEM)); + goto err; + } + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&type)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (qpol_type_get_name(policy->p, type, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + for (; !qpol_iterator_end(iter2); qpol_iterator_next(iter2)) { + if (qpol_iterator_get_item(iter2, (void **)&type)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (qpol_type_get_name(policy->p, type, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "-%s ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&iter2); + if (iter_sz + iter2_sz > 1) { + if (apol_str_append(&tmp, &tmp_sz, "} ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + } + + /* target type set */ + if (qpol_syn_avrule_get_target_type_set(policy->p, rule, &set)) { + error = errno; + goto err; + } + if (qpol_type_set_get_is_star(policy->p, set, &star)) { + error = errno; + goto err; + } + if (star) { + if (apol_str_append(&tmp, &tmp_sz, "* ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } else { + if (qpol_type_set_get_is_comp(policy->p, set, &comp)) { + error = errno; + goto err; + } + if (comp) { + if (apol_str_append(&tmp, &tmp_sz, "~")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + if (qpol_type_set_get_included_types_iter(policy->p, set, &iter)) { + error = errno; + goto err; + } + if (qpol_type_set_get_subtracted_types_iter(policy->p, set, &iter2)) { + error = errno; + goto err; + } + if (qpol_iterator_get_size(iter, &iter_sz) || qpol_iterator_get_size(iter2, &iter2_sz)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (qpol_syn_avrule_get_is_target_self(policy->p, rule, &self)) { + error = errno; + goto err; + } + if (iter_sz + iter2_sz + self > 1) { + if (apol_str_append(&tmp, &tmp_sz, "{ ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&type)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (qpol_type_get_name(policy->p, type, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + for (; !qpol_iterator_end(iter2); qpol_iterator_next(iter2)) { + if (qpol_iterator_get_item(iter2, (void **)&type)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (qpol_type_get_name(policy->p, type, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "-%s ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&iter2); + if (self) { + if (apol_str_append(&tmp, &tmp_sz, "self ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + if (iter_sz + iter2_sz + self > 1) { + if (apol_str_append(&tmp, &tmp_sz, "} ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + } + + if (apol_str_append(&tmp, &tmp_sz, ": ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + /* object classes */ + if (qpol_syn_avrule_get_class_iter(policy->p, rule, &iter)) { + error = errno; + goto err; + } + if (qpol_iterator_get_size(iter, &iter_sz)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (iter_sz > 1) { + if (apol_str_append(&tmp, &tmp_sz, "{ ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&obj_class)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (qpol_class_get_name(policy->p, obj_class, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + qpol_iterator_destroy(&iter); + if (iter_sz > 1) { + if (apol_str_append(&tmp, &tmp_sz, "} ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + + /* permissions */ + if (qpol_syn_avrule_get_perm_iter(policy->p, rule, &iter)) { + error = errno; + goto err; + } + if (qpol_iterator_get_size(iter, &iter_sz)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (iter_sz > 1) { + if (apol_str_append(&tmp, &tmp_sz, "{ ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + qpol_iterator_destroy(&iter); + if (iter_sz > 1) { + if (apol_str_append(&tmp, &tmp_sz, "} ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + + if (apol_str_append(&tmp, &tmp_sz, ";")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + return tmp; + + err: + free(tmp); + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&iter2); + errno = error; + return NULL; +} diff --git a/libapol/src/bool-query.c b/libapol/src/bool-query.c new file mode 100644 index 0000000..8147c3a --- /dev/null +++ b/libapol/src/bool-query.c @@ -0,0 +1,111 @@ +/** + * @file + * + * Provides a way for setools to make queries about conditional + * booleans within a policy. The caller obtains a query object, fills + * in its parameters, and then runs the query; it obtains a vector of + * results. Searches are conjunctive -- all fields of the search + * query must match for a datum to be added to the results query. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" + +#include <errno.h> + +struct apol_bool_query +{ + char *bool_name; + unsigned int flags; + regex_t *regex; +}; + +/******************** booleans queries ********************/ + +int apol_bool_get_by_query(const apol_policy_t * p, apol_bool_query_t * b, apol_vector_t ** v) +{ + qpol_iterator_t *iter; + int retval = -1; + *v = NULL; + if (qpol_policy_get_bool_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_bool_t *qbool; + if (qpol_iterator_get_item(iter, (void **)&qbool) < 0) { + goto cleanup; + } + if (b != NULL) { + const char *bool_name; + int compval; + if (qpol_bool_get_name(p->p, qbool, &bool_name) < 0) { + goto cleanup; + } + compval = apol_compare(p, bool_name, b->bool_name, b->flags, &(b->regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + } + if (apol_vector_append(*v, qbool)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_bool_query_t *apol_bool_query_create(void) +{ + return calloc(1, sizeof(apol_bool_query_t)); +} + +void apol_bool_query_destroy(apol_bool_query_t ** b) +{ + if (*b != NULL) { + free((*b)->bool_name); + apol_regex_destroy(&(*b)->regex); + free(*b); + *b = NULL; + } +} + +int apol_bool_query_set_bool(const apol_policy_t * p, apol_bool_query_t * b, const char *name) +{ + return apol_query_set(p, &b->bool_name, &b->regex, name); +} + +int apol_bool_query_set_regex(const apol_policy_t * p, apol_bool_query_t * b, int is_regex) +{ + return apol_query_set_regex(p, &b->flags, is_regex); +} diff --git a/libapol/src/bst.c b/libapol/src/bst.c new file mode 100644 index 0000000..df0beb6 --- /dev/null +++ b/libapol/src/bst.c @@ -0,0 +1,352 @@ +/** + * @file + * Contains the implementation of a generic binary search tree. The + * tree is implemented as a red-black tree, as inspired by Julienne + * Walker (http://eternallyconfuzzled.com/tuts/redblack.html). + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <apol/bst.h> +#include <apol/vector.h> +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vector-internal.h" + +typedef struct bst_node +{ + void *elem; + int is_red; + struct bst_node *child[2]; +} bst_node_t; + +/** + * Generic binary search tree structure. Stores elements as void*. + */ +struct apol_bst +{ + /** Comparison function for nodes. */ + apol_bst_comp_func *cmp; + /** Destroy function for the nodes, or NULL to not free each node. */ + apol_bst_free_func *fr; + /** The number of elements currently stored in the bst. */ + size_t size; + /** Pointer to top of the tree. */ + bst_node_t *head; +}; + +apol_bst_t *apol_bst_create(apol_bst_comp_func * cmp, apol_bst_free_func * fr) +{ + apol_bst_t *b = NULL; + if ((b = calloc(1, sizeof(*b))) == NULL) { + return NULL; + } + b->cmp = cmp; + b->fr = fr; + return b; +} + +/** + * Free the data stored within a bst node, recurse through the node's + * children, and then the node itself. + * + * @param node Node to free. If NULL then do stop recursing. + * @param fr Callback to free a node's data. If NULL then do not free + * the data. + */ +static void bst_node_free(bst_node_t * node, apol_bst_free_func * fr) +{ + if (node != NULL) { + if (fr != NULL) { + fr(node->elem); + } + bst_node_free(node->child[0], fr); + bst_node_free(node->child[1], fr); + free(node); + } +} + +void apol_bst_destroy(apol_bst_t ** b) +{ + if (!b || !(*b)) + return; + bst_node_free((*b)->head, (*b)->fr); + (*b)->head = NULL; + free(*b); + *b = NULL; +} + +/** + * Given a BST node, traverse the node infix, appending the node's + * element to vector v. + * + * @param node BST node to recurse. + * @param v Vector to which append. + * + * @return 0 on success, < 0 on error. + */ +static int bst_node_to_vector(bst_node_t * node, apol_vector_t * v) +{ + int retval; + if (node == NULL) { + return 0; + } + if ((retval = bst_node_to_vector(node->child[0], v)) < 0) { + return retval; + } + if ((retval = apol_vector_append(v, node->elem)) < 0) { + return retval; + } + return bst_node_to_vector(node->child[1], v); +} + +apol_vector_t *apol_bst_get_vector(apol_bst_t * b, int change_owner) +{ + apol_vector_t *v = NULL; + if (!b) { + errno = EINVAL; + return NULL; + } + if ((v = apol_vector_create_with_capacity(b->size, NULL)) == NULL) { + return NULL; + } + if (bst_node_to_vector(b->head, v) < 0) { + int error = errno; + apol_vector_destroy(&v); + errno = error; + return NULL; + } + if (change_owner) { + vector_set_free_func(v, b->fr); + b->fr = NULL; + } + return v; +} + +size_t apol_bst_get_size(const apol_bst_t * b) +{ + if (!b) { + errno = EINVAL; + return 0; + } else { + return b->size; + } +} + +int apol_bst_get_element(const apol_bst_t * b, const void *elem, void *data, void **result) +{ + bst_node_t *node; + int compval; + if (!b || !result) { + errno = EINVAL; + return -1; + } + node = b->head; + while (node != NULL) { + if (b->cmp != NULL) { + compval = b->cmp(node->elem, elem, data); + } else { + char *p1 = (char *)node->elem; + char *p2 = (char *)elem; + if (p1 < p2) { + compval = -1; + } else if (p1 > p2) { + compval = 1; + } else { + compval = 0; + } + } + if (compval == 0) { + *result = node->elem; + return 0; + } else if (compval > 0) { + node = node->child[0]; + } else { + node = node->child[1]; + } + } + return -1; +} + +/** + * Allocate and return a new BST node, with data set to elem and color + * to red. Also increment the tree's size. + * + * @param b BST size to increment. + * @param elem Value for the node. + * + * @return Allocated BST node, which the caller must insert, or NULL + * on error. + */ +static bst_node_t *bst_node_make(apol_bst_t * b, void *elem) +{ + bst_node_t *new_node; + if ((new_node = calloc(1, sizeof(*new_node))) == NULL) { + return NULL; + } + new_node->elem = elem; + new_node->is_red = 1; + b->size++; + return new_node; +} + +/** + * Determines if a node is red or not. + * + * @param node Node to check. If NULL then treat the node as black. + * + * @return 0 if the node is black, 1 if red. + */ +static int bst_node_is_red(bst_node_t * node) +{ + return node != NULL && node->is_red; +} + +static bst_node_t *bst_rotate_single(bst_node_t * root, int dir) +{ + bst_node_t *save = root->child[!dir]; + root->child[!dir] = save->child[dir]; + save->child[dir] = root; + root->is_red = 1; + save->is_red = 0; + return save; +} + +static bst_node_t *bst_rotate_double(bst_node_t * root, int dir) +{ + root->child[!dir] = bst_rotate_single(root->child[!dir], !dir); + return bst_rotate_single(root, dir); +} + +static bst_node_t *bst_insert_recursive(apol_bst_t * b, bst_node_t * root, void **elem, void *data, apol_bst_free_func * fr, + int *not_uniq) +{ + int compval, dir; + if (root == NULL) { + if ((root = bst_node_make(b, *elem)) == NULL) { + *not_uniq = -1; + return NULL; + } + *not_uniq = 0; + } else { + if (b->cmp != NULL) { + compval = b->cmp(root->elem, *elem, data); + } else { + char *p1 = (char *)root->elem; + char *p2 = (char *)(*elem); + if (p1 < p2) { + compval = -1; + } else if (p1 > p2) { + compval = 1; + } else { + compval = 0; + } + } + if (compval == 0) { + /* already exists */ + if (fr != NULL) { + fr(*elem); + } + *elem = root->elem; + *not_uniq = 1; + return root; + } else if (compval > 0) { + dir = 0; + } else { + dir = 1; + } + root->child[dir] = bst_insert_recursive(b, root->child[dir], elem, data, fr, not_uniq); + if (*not_uniq != 0) { + return root; + } + + /* rebalance tree */ + if (bst_node_is_red(root->child[dir])) { + if (bst_node_is_red(root->child[!dir])) { + /* recolor myself and children. note + * that this can't be reached if a + * child is NULL */ + root->is_red = 1; + root->child[0]->is_red = 0; + root->child[1]->is_red = 0; + } else { + if (bst_node_is_red(root->child[dir]->child[dir])) { + root = bst_rotate_single(root, !dir); + } else if (bst_node_is_red(root->child[dir]->child[!dir])) { + root = bst_rotate_double(root, !dir); + } + } + } + } + return root; +} + +int apol_bst_insert(apol_bst_t * b, void *elem, void *data) +{ + int retval = -1; + if (!b || !elem) { + errno = EINVAL; + return -1; + } + b->head = bst_insert_recursive(b, b->head, &elem, data, NULL, &retval); + if (retval >= 0) { + b->head->is_red = 0; + } + return retval; +} + +int apol_bst_insert_and_get(apol_bst_t * b, void **elem, void *data) +{ + int retval = -1; + if (!b || !elem) { + errno = EINVAL; + return -1; + } + b->head = bst_insert_recursive(b, b->head, elem, data, b->fr, &retval); + if (retval >= 0) { + b->head->is_red = 0; + } + return retval; +} + +static int bst_inorder_map(const bst_node_t * node, int (*fn) (void *, void *), void *data) +{ + int retval; + if (node == NULL) { + return 0; + } + if ((retval = bst_inorder_map(node->child[0], fn, data)) < 0) { + return retval; + } + if ((retval = fn(node->elem, data)) < 0) { + return retval; + } + return bst_inorder_map(node->child[1], fn, data); +} + +int apol_bst_inorder_map(const apol_bst_t * b, int (*fn) (void *, void *), void *data) +{ + if (b == NULL || fn == NULL) + return -1; + return bst_inorder_map(b->head, fn, data); +} diff --git a/libapol/src/class-perm-query.c b/libapol/src/class-perm-query.c new file mode 100644 index 0000000..938994d --- /dev/null +++ b/libapol/src/class-perm-query.c @@ -0,0 +1,327 @@ +/** + * @file + * + * Provides a way for setools to make queries about classes, commons, + * and permissions within a policy. The caller obtains a query + * object, fills in its parameters, and then runs the query; it + * obtains a vector of results. Searches are conjunctive -- all + * fields of the search query must match for a datum to be added to + * the results query. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" +#include <errno.h> +#include <string.h> + +struct apol_class_query +{ + char *class_name, *common_name; + unsigned int flags; + regex_t *class_regex, *common_regex; +}; + +struct apol_common_query +{ + char *common_name; + unsigned int flags; + regex_t *regex; +}; + +struct apol_perm_query +{ + char *perm_name; + unsigned int flags; + regex_t *regex; +}; + +/******************** class queries ********************/ + +int apol_class_get_by_query(const apol_policy_t * p, apol_class_query_t * c, apol_vector_t ** v) +{ + qpol_iterator_t *iter = NULL, *perm_iter = NULL; + int retval = -1, append_class; + *v = NULL; + if (qpol_policy_get_class_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_class_t *class_datum; + if (qpol_iterator_get_item(iter, (void **)&class_datum) < 0) { + goto cleanup; + } + append_class = 1; + if (c != NULL) { + const char *class_name, *common_name = NULL; + const qpol_common_t *common_datum; + int compval; + if (qpol_class_get_name(p->p, class_datum, &class_name) < 0) { + goto cleanup; + } + compval = apol_compare(p, class_name, c->class_name, c->flags, &(c->class_regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + if (qpol_class_get_common(p->p, class_datum, &common_datum) < 0) { + goto cleanup; + } + if (common_datum == NULL) { + if (c->common_name != NULL && c->common_name[0] != '\0') { + continue; + } + } else { + if (qpol_common_get_name(p->p, common_datum, &common_name) < 0) { + goto cleanup; + } + compval = apol_compare(p, common_name, c->common_name, c->flags, &(c->common_regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + } + } + if (append_class && apol_vector_append(*v, class_datum)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&perm_iter); + return retval; +} + +apol_class_query_t *apol_class_query_create(void) +{ + return calloc(1, sizeof(apol_class_query_t)); +} + +void apol_class_query_destroy(apol_class_query_t ** c) +{ + if (*c != NULL) { + free((*c)->class_name); + free((*c)->common_name); + apol_regex_destroy(&(*c)->class_regex); + apol_regex_destroy(&(*c)->common_regex); + free(*c); + *c = NULL; + } +} + +int apol_class_query_set_class(const apol_policy_t * p, apol_class_query_t * c, const char *name) +{ + return apol_query_set(p, &c->class_name, &c->class_regex, name); +} + +int apol_class_query_set_common(const apol_policy_t * p, apol_class_query_t * c, const char *name) +{ + return apol_query_set(p, &c->common_name, &c->common_regex, name); +} + +int apol_class_query_set_regex(const apol_policy_t * p, apol_class_query_t * c, int is_regex) +{ + return apol_query_set_regex(p, &c->flags, is_regex); +} + +/******************** common queries ********************/ + +int apol_common_get_by_query(const apol_policy_t * p, apol_common_query_t * c, apol_vector_t ** v) +{ + qpol_iterator_t *iter = NULL; + int retval = -1; + *v = NULL; + if (qpol_policy_get_common_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_common_t *common_datum; + if (qpol_iterator_get_item(iter, (void **)&common_datum) < 0) { + goto cleanup; + } + if (c != NULL) { + const char *common_name = NULL; + int compval; + if (qpol_common_get_name(p->p, common_datum, &common_name) < 0) { + goto cleanup; + } + compval = apol_compare(p, common_name, c->common_name, c->flags, &(c->regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + } + if (apol_vector_append(*v, common_datum)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_common_query_t *apol_common_query_create(void) +{ + return calloc(1, sizeof(apol_common_query_t)); +} + +void apol_common_query_destroy(apol_common_query_t ** c) +{ + if (*c != NULL) { + free((*c)->common_name); + apol_regex_destroy(&(*c)->regex); + free(*c); + *c = NULL; + } +} + +int apol_common_query_set_common(const apol_policy_t * p, apol_common_query_t * c, const char *name) +{ + return apol_query_set(p, &c->common_name, &c->regex, name); +} + +int apol_common_query_set_regex(const apol_policy_t * p, apol_common_query_t * c, int is_regex) +{ + return apol_query_set_regex(p, &c->flags, is_regex); +} + +/******************** permission queries ********************/ + +int apol_perm_get_by_query(const apol_policy_t * p, apol_perm_query_t * pq, apol_vector_t ** v) +{ + qpol_iterator_t *class_iter = NULL, *common_iter = NULL, *perm_iter = NULL; + int retval = -1, compval; + char *perm_name; + *v = NULL; + if (qpol_policy_get_class_iter(p->p, &class_iter) < 0 || qpol_policy_get_common_iter(p->p, &common_iter) < 0) { + goto cleanup; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(class_iter); qpol_iterator_next(class_iter)) { + qpol_class_t *class_datum; + if (qpol_iterator_get_item(class_iter, (void **)&class_datum) < 0 || + qpol_class_get_perm_iter(p->p, class_datum, &perm_iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) { + if (qpol_iterator_get_item(perm_iter, (void **)&perm_name) < 0) { + goto cleanup; + } + if (pq == NULL) { + compval = 1; + } else { + compval = apol_compare(p, perm_name, pq->perm_name, pq->flags, &(pq->regex)); + } + if (compval < 0) { + goto cleanup; + } else if (compval == 1 && apol_vector_append_unique(*v, perm_name, apol_str_strcmp, NULL) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + qpol_iterator_destroy(&perm_iter); + } + + for (; !qpol_iterator_end(common_iter); qpol_iterator_next(common_iter)) { + qpol_common_t *common_datum; + if (qpol_iterator_get_item(common_iter, (void **)&common_datum) < 0 || + qpol_common_get_perm_iter(p->p, common_datum, &perm_iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) { + if (qpol_iterator_get_item(perm_iter, (void **)&perm_name) < 0) { + goto cleanup; + } + if (pq == NULL) { + compval = 1; + } else { + compval = apol_compare(p, perm_name, pq->perm_name, pq->flags, &(pq->regex)); + } + if (compval < 0) { + goto cleanup; + } else if (compval == 1 && apol_vector_append_unique(*v, perm_name, apol_str_strcmp, NULL) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + qpol_iterator_destroy(&perm_iter); + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&class_iter); + qpol_iterator_destroy(&common_iter); + qpol_iterator_destroy(&perm_iter); + return retval; +} + +apol_perm_query_t *apol_perm_query_create(void) +{ + return calloc(1, sizeof(apol_perm_query_t)); +} + +void apol_perm_query_destroy(apol_perm_query_t ** pq) +{ + if (*pq != NULL) { + free((*pq)->perm_name); + apol_regex_destroy(&(*pq)->regex); + free(*pq); + *pq = NULL; + } +} + +int apol_perm_query_set_perm(const apol_policy_t * p, apol_perm_query_t * pq, const char *name) +{ + return apol_query_set(p, &pq->perm_name, &pq->regex, name); +} + +int apol_perm_query_set_regex(const apol_policy_t * p, apol_perm_query_t * pq, int is_regex) +{ + return apol_query_set_regex(p, &pq->flags, is_regex); +} diff --git a/libapol/src/condrule-query.c b/libapol/src/condrule-query.c new file mode 100644 index 0000000..451b617 --- /dev/null +++ b/libapol/src/condrule-query.c @@ -0,0 +1,182 @@ +/** + * @file + * + * Provides a way for setools to make queries about conditional + * expressions rules within a policy. The caller obtains a query + * object, fills in its parameters, and then runs the query; it + * obtains a vector of results. Searches are conjunctive -- all + * fields of the search query must match for a datum to be added to + * the results query. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" +#include <errno.h> + +struct apol_cond_query +{ + char *bool_name; + unsigned int flags; + regex_t *regex; +}; + +int apol_cond_get_by_query(const apol_policy_t * p, apol_cond_query_t * c, apol_vector_t ** v) +{ + qpol_iterator_t *iter = NULL; + int retval = -1; + *v = NULL; + + if (qpol_policy_get_cond_iter(p->p, &iter) < 0) { + goto cleanup; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_cond_t *cond; + if (qpol_iterator_get_item(iter, (void **)&cond) < 0) { + goto cleanup; + } + if (c != NULL) { + int keep_cond = apol_compare_cond_expr(p, cond, c->bool_name, c->flags, &c->regex); + if (keep_cond < 0) { + goto cleanup; + } else if (keep_cond == 0) { + continue; + } + } + if (apol_vector_append(*v, cond)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_cond_query_t *apol_cond_query_create(void) +{ + return calloc(1, sizeof(apol_cond_query_t)); +} + +void apol_cond_query_destroy(apol_cond_query_t ** c) +{ + if (*c != NULL) { + free((*c)->bool_name); + apol_regex_destroy(&(*c)->regex); + free(*c); + *c = NULL; + } +} + +int apol_cond_query_set_bool(const apol_policy_t * p, apol_cond_query_t * c, const char *name) +{ + return apol_query_set(p, &c->bool_name, &c->regex, name); +} + +int apol_cond_query_set_regex(const apol_policy_t * p, apol_cond_query_t * c, int is_regex) +{ + return apol_query_set_regex(p, &c->flags, is_regex); +} + +char *apol_cond_expr_render(const apol_policy_t * p, const qpol_cond_t * cond) +{ + qpol_iterator_t *iter = NULL; + qpol_cond_expr_node_t *expr = NULL; + char *tmp = NULL; + const char *bool_name = NULL; + int error = 0; + size_t tmp_sz = 0, i; + uint32_t expr_type = 0; + qpol_bool_t *cond_bool = NULL; + + if (!p || !cond) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + if (qpol_cond_get_expr_node_iter(p->p, cond, &iter) < 0) { + error = errno; + goto err; + } + + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&expr)) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + if (qpol_cond_expr_node_get_expr_type(p->p, expr, &expr_type)) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + if (expr_type != QPOL_COND_EXPR_BOOL) { + if (apol_str_append(&tmp, &tmp_sz, apol_cond_expr_type_to_str(expr_type))) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + } else { + if (qpol_cond_expr_node_get_bool(p->p, expr, &cond_bool)) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + if (qpol_bool_get_name(p->p, cond_bool, &bool_name)) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + if (apol_str_append(&tmp, &tmp_sz, bool_name)) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + } + if (apol_str_append(&tmp, &tmp_sz, " ")) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + } + + /* remove trailing space */ + i = strlen(tmp); + if (i > 1) { + tmp[i - 1] = '\0'; + } + qpol_iterator_destroy(&iter); + return tmp; + + err: + qpol_iterator_destroy(&iter); + free(tmp); + errno = error; + return NULL; +} diff --git a/libapol/src/constraint-query.c b/libapol/src/constraint-query.c new file mode 100644 index 0000000..5495975 --- /dev/null +++ b/libapol/src/constraint-query.c @@ -0,0 +1,217 @@ +/** + * @file + * + * Provides a way for setools to make queries about constraint and + * validatetrans statements within a policy. The caller obtains a + * query object, fills in its parameters, and then runs the query; it + * obtains a vector of results. Searches are conjunctive -- all + * fields of the search query must match for a datum to be added to + * the results. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" +#include <errno.h> +#include <string.h> + +struct apol_constraint_query +{ + char *class_name, *perm_name; + unsigned int flags; + regex_t *class_regex, *perm_regex; +}; + +struct apol_validatetrans_query +{ + char *class_name; + unsigned int flags; + regex_t *regex; +}; + +/******************** constraint queries ********************/ + +int apol_constraint_get_by_query(const apol_policy_t * p, apol_constraint_query_t * c, apol_vector_t ** v) +{ + qpol_iterator_t *iter = NULL, *perm_iter = NULL; + int retval = -1; + *v = NULL; + if (qpol_policy_get_constraint_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_constraint_t *constraint; + if (qpol_iterator_get_item(iter, (void **)&constraint) < 0) { + goto cleanup; + } + if (c != NULL) { + const qpol_class_t *class_datum; + const char *class_name; + int compval; + if (qpol_constraint_get_class(p->p, constraint, &class_datum) < 0 || + qpol_class_get_name(p->p, class_datum, &class_name) < 0) { + goto cleanup; + } + compval = apol_compare(p, class_name, c->class_name, c->flags, &(c->class_regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + free(constraint); + continue; + } + + if (qpol_constraint_get_perm_iter(p->p, constraint, &perm_iter) < 0) { + goto cleanup; + } + compval = apol_compare_iter(p, perm_iter, c->perm_name, c->flags, &(c->perm_regex), 1); + qpol_iterator_destroy(&perm_iter); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + free(constraint); + continue; + } + } + if (apol_vector_append(*v, constraint)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&perm_iter); + return retval; +} + +apol_constraint_query_t *apol_constraint_query_create(void) +{ + return calloc(1, sizeof(apol_constraint_query_t)); +} + +void apol_constraint_query_destroy(apol_constraint_query_t ** c) +{ + if (*c != NULL) { + free((*c)->class_name); + free((*c)->perm_name); + apol_regex_destroy(&(*c)->class_regex); + apol_regex_destroy(&(*c)->perm_regex); + free(*c); + *c = NULL; + } +} + +int apol_constraint_query_set_class(const apol_policy_t * p, apol_constraint_query_t * c, const char *name) +{ + return apol_query_set(p, &c->class_name, &c->class_regex, name); +} + +int apol_constraint_query_set_perm(const apol_policy_t * p, apol_constraint_query_t * c, const char *name) +{ + return apol_query_set(p, &c->perm_name, &c->perm_regex, name); +} + +int apol_constraint_query_set_regex(const apol_policy_t * p, apol_constraint_query_t * c, int is_regex) +{ + return apol_query_set_regex(p, &c->flags, is_regex); +} + +/******************** validatetrans queries ********************/ + +int apol_validatetrans_get_by_query(const apol_policy_t * p, apol_validatetrans_query_t * vt, apol_vector_t ** v) +{ + qpol_iterator_t *iter = NULL; + int retval = -1; + *v = NULL; + if (qpol_policy_get_validatetrans_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_validatetrans_t *validatetrans; + if (qpol_iterator_get_item(iter, (void **)&validatetrans) < 0) { + goto cleanup; + } + if (vt != NULL) { + const qpol_class_t *class_datum; + const char *class_name; + int compval; + if (qpol_validatetrans_get_class(p->p, validatetrans, &class_datum) < 0 || + qpol_class_get_name(p->p, class_datum, &class_name) < 0) { + goto cleanup; + } + compval = apol_compare(p, class_name, vt->class_name, vt->flags, &(vt->regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + free(validatetrans); + continue; + } + } + if (apol_vector_append(*v, validatetrans)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_validatetrans_query_t *apol_validatetrans_query_create(void) +{ + return calloc(1, sizeof(apol_validatetrans_query_t)); +} + +void apol_validatetrans_query_destroy(apol_validatetrans_query_t ** vt) +{ + if (*vt != NULL) { + free((*vt)->class_name); + apol_regex_destroy(&(*vt)->regex); + free(*vt); + *vt = NULL; + } +} + +int apol_validatetrans_query_set_class(const apol_policy_t * p, apol_validatetrans_query_t * vt, const char *name) +{ + return apol_query_set(p, &vt->class_name, &vt->regex, name); +} + +int apol_validatetrans_query_set_regex(const apol_policy_t * p, apol_validatetrans_query_t * vt, int is_regex) +{ + return apol_query_set_regex(p, &vt->flags, is_regex); +} diff --git a/libapol/src/context-query.c b/libapol/src/context-query.c new file mode 100644 index 0000000..90c7fbe --- /dev/null +++ b/libapol/src/context-query.c @@ -0,0 +1,477 @@ +/** + * @file + * Implementation for querying aspects of a context. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" + +#include <assert.h> +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#include <apol/render.h> + +struct apol_context +{ + char *user, *role, *type; + apol_mls_range_t *range; +}; + +apol_context_t *apol_context_create(void) +{ + return calloc(1, sizeof(apol_context_t)); +} + +apol_context_t *apol_context_create_from_qpol_context(const apol_policy_t * p, const qpol_context_t * context) +{ + apol_context_t *c = NULL; + const qpol_user_t *user; + const qpol_role_t *role; + const qpol_type_t *type; + const qpol_mls_range_t *range; + const char *user_name, *role_name, *type_name; + apol_mls_range_t *apol_range = NULL; + if ((c = apol_context_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto err; + } + if (qpol_context_get_user(p->p, context, &user) < 0 || + qpol_context_get_role(p->p, context, &role) < 0 || + qpol_context_get_type(p->p, context, &type) < 0 || qpol_context_get_range(p->p, context, &range) < 0) { + goto err; + } + if (qpol_user_get_name(p->p, user, &user_name) < 0 || + qpol_role_get_name(p->p, role, &role_name) < 0 || qpol_type_get_name(p->p, type, &type_name) < 0) { + goto err; + } + if (qpol_policy_has_capability(p->p, QPOL_CAP_MLS)) { + /* if the policy is MLS then convert the range, else + * rely upon the default value of NULL */ + if ((apol_range = apol_mls_range_create_from_qpol_mls_range(p, range)) == NULL) { + goto err; + } + } + if (apol_context_set_user(p, c, user_name) < 0 || + apol_context_set_role(p, c, role_name) < 0 || + apol_context_set_type(p, c, type_name) < 0 || apol_context_set_range(p, c, apol_range) < 0) { + goto err; + } + return c; + err: + apol_mls_range_destroy(&apol_range); + apol_context_destroy(&c); + return NULL; +} + +apol_context_t *apol_context_create_from_literal(const char *context_string) +{ + apol_context_t *c = NULL; + bool is_context_compiled = false; + regex_t context_regex; + const size_t nmatch = 5; + regmatch_t pmatch[nmatch]; + + if ((c = apol_context_create()) == NULL) { + goto err; + } + + if (regcomp(&context_regex, "^([^:]*):([^:]*):([^:]*):?(.*)$", REG_EXTENDED) != 0) { + goto err; + } + is_context_compiled = true; + + if (regexec(&context_regex, context_string, nmatch, pmatch, 0) != 0) { + errno = EIO; + goto err; + } + + const char *s; + size_t len; + + assert(pmatch[1].rm_so == 0); + s = context_string + pmatch[1].rm_so; + len = pmatch[1].rm_eo - pmatch[1].rm_so; // no +1 to avoid copying colon + if (len != 0 && *s != '*' && (c->user = strndup(s, len)) == NULL) { + goto err; + } + + assert(pmatch[2].rm_so != -1); + s = context_string + pmatch[2].rm_so; + len = pmatch[2].rm_eo - pmatch[2].rm_so; // no +1 to avoid copying colon + if (len != 0 && *s != '*' && (c->role = strndup(s, len)) == NULL) { + goto err; + } + + assert(pmatch[3].rm_so != -1); + s = context_string + pmatch[3].rm_so; + len = pmatch[3].rm_eo - pmatch[3].rm_so; // no +1 to avoid copying colon + if (len != 0 && *s != '*' && (c->type = strndup(s, len)) == NULL) { + goto err; + } + + if (pmatch[4].rm_so != -1) { + s = context_string + pmatch[4].rm_so; + len = pmatch[4].rm_eo - pmatch[4].rm_so; + if (len != 0 && *s != '*' && (c->range = apol_mls_range_create_from_literal(s)) == NULL) { + goto err; + } + } + + regfree(&context_regex); + return c; + + err: + apol_context_destroy(&c); + if (is_context_compiled) { + regfree(&context_regex); + } + return NULL; +} + +void apol_context_destroy(apol_context_t ** context) +{ + if (*context != NULL) { + free((*context)->user); + free((*context)->role); + free((*context)->type); + apol_mls_range_destroy(&((*context)->range)); + free(*context); + *context = NULL; + } +} + +int apol_context_set_user(const apol_policy_t * p, apol_context_t * context, const char *user) +{ + if (context == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if (user != context->user) { + free(context->user); + context->user = NULL; + if (user != NULL && (context->user = strdup(user)) == NULL) { + ERR(p, "%s", strerror(errno)); + return -1; + } + } + return 0; +} + +int apol_context_set_role(const apol_policy_t * p, apol_context_t * context, const char *role) +{ + if (context == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if (role != context->role) { + free(context->role); + context->role = NULL; + if (role != NULL && (context->role = strdup(role)) == NULL) { + ERR(p, "%s", strerror(errno)); + return -1; + } + } + return 0; +} + +int apol_context_set_type(const apol_policy_t * p, apol_context_t * context, const char *type) +{ + if (context == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if (type != context->type) { + free(context->type); + context->type = NULL; + if (type != NULL && (context->type = strdup(type)) == NULL) { + ERR(p, "%s", strerror(errno)); + return -1; + } + } + return 0; +} + +int apol_context_set_range(const apol_policy_t * p, apol_context_t * context, apol_mls_range_t * range) +{ + if (context == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if (range != context->range) { + apol_mls_range_destroy(&(context->range)); + context->range = range; + } + return 0; +} + +const char *apol_context_get_user(const apol_context_t * context) +{ + if (context == NULL) { + errno = EINVAL; + return NULL; + } + return context->user; +} + +const char *apol_context_get_role(const apol_context_t * context) +{ + if (context == NULL) { + errno = EINVAL; + return NULL; + } + return context->role; +} + +const char *apol_context_get_type(const apol_context_t * context) +{ + if (context == NULL) { + errno = EINVAL; + return NULL; + } + return context->type; +} + +const apol_mls_range_t *apol_context_get_range(const apol_context_t * context) +{ + if (context == NULL) { + errno = EINVAL; + return NULL; + } + return context->range; +} + +int apol_context_compare(const apol_policy_t * p, const apol_context_t * target, const apol_context_t * search, + unsigned int range_compare_type) +{ + uint32_t value0, value1; + if (p == NULL || target == NULL || search == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if (target->user != NULL && search->user != NULL) { + const qpol_user_t *user0, *user1; + if (qpol_policy_get_user_by_name(p->p, + target->user, &user0) < 0 || + qpol_policy_get_user_by_name(p->p, + search->user, &user1) < 0 || + qpol_user_get_value(p->p, user0, &value0) < 0 || qpol_user_get_value(p->p, user1, &value1) < 0) { + return -1; + } + if (value0 != value1) { + return 0; + } + } + if (target->role != NULL && search->role != NULL) { + const qpol_role_t *role0, *role1; + if (qpol_policy_get_role_by_name(p->p, + target->role, &role0) < 0 || + qpol_policy_get_role_by_name(p->p, + search->role, &role1) < 0 || + qpol_role_get_value(p->p, role0, &value0) < 0 || qpol_role_get_value(p->p, role1, &value1) < 0) { + return -1; + } + if (value0 != value1) { + return 0; + } + } + if (target->type != NULL && search->type != NULL) { + const qpol_type_t *type0, *type1; + if (qpol_policy_get_type_by_name(p->p, + target->type, &type0) < 0 || + qpol_policy_get_type_by_name(p->p, + search->type, &type1) < 0 || + qpol_type_get_value(p->p, type0, &value0) < 0 || qpol_type_get_value(p->p, type1, &value1) < 0) { + return -1; + } + if (value0 != value1) { + return 0; + } + } + if (target->range != NULL && search->range != NULL) { + return apol_mls_range_compare(p, target->range, search->range, range_compare_type); + } + return 1; +} + +int apol_context_validate(const apol_policy_t * p, const apol_context_t * context) +{ + if (context == NULL || + context->user == NULL || + context->role == NULL || context->type == NULL || (apol_policy_is_mls(p) && context->range == NULL)) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + return apol_context_validate_partial(p, context); +} + +int apol_context_validate_partial(const apol_policy_t * p, const apol_context_t * context) +{ + apol_user_query_t *user_query = NULL; + apol_role_query_t *role_query = NULL; + apol_vector_t *user_v = NULL, *role_v = NULL; + const qpol_user_t *user; + const qpol_type_t *type; + const qpol_mls_range_t *user_range; + apol_mls_range_t *user_apol_range = NULL; + int retval = -1, retval2; + + if (context == NULL) { + return 1; + } + if (context->user != NULL) { + if ((user_query = apol_user_query_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + } + if (apol_user_query_set_user(p, user_query, context->user) < 0 || + (context->role != NULL && apol_user_query_set_role(p, user_query, context->role) < 0) || + apol_user_get_by_query(p, user_query, &user_v) < 0) { + goto cleanup; + } + if (apol_vector_get_size(user_v) == 0) { + retval = 0; + goto cleanup; + } + } + if (context->role != NULL) { + if ((role_query = apol_role_query_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + } + if (apol_role_query_set_role(p, role_query, context->role) < 0 || + (context->type != NULL && apol_role_query_set_type(p, role_query, context->type) < 0) || + apol_role_get_by_query(p, role_query, &role_v) < 0) { + goto cleanup; + } + if (apol_vector_get_size(role_v) == 0) { + retval = 0; + goto cleanup; + } + } + if (context->type != NULL) { + if (qpol_policy_get_type_by_name(p->p, context->type, &type) < 0) { + retval = 0; + goto cleanup; + } + } + if (apol_policy_is_mls(p) && context->range != NULL) { + retval2 = apol_mls_range_validate(p, context->range); + if (retval2 != 1) { + retval = retval2; + goto cleanup; + } + /* next check that the user has access to this context */ + if (context->user != NULL) { + if (qpol_policy_get_user_by_name(p->p, context->user, &user) < 0 || + qpol_user_get_range(p->p, user, &user_range) < 0) { + goto cleanup; + } + user_apol_range = apol_mls_range_create_from_qpol_mls_range(p, user_range); + if (user_apol_range == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + retval2 = apol_mls_range_compare(p, user_apol_range, context->range, APOL_QUERY_SUB); + if (retval2 != 1) { + retval = retval2; + goto cleanup; + } + } + } + retval = 1; + cleanup: + apol_user_query_destroy(&user_query); + apol_role_query_destroy(&role_query); + apol_vector_destroy(&user_v); + apol_vector_destroy(&role_v); + apol_mls_range_destroy(&user_apol_range); + return retval; +} + +char *apol_context_render(const apol_policy_t * p, const apol_context_t * context) +{ + char *buf = NULL, *range_str = NULL; + size_t buf_sz = 0; + + if (context == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + if (p == NULL && !apol_mls_range_is_literal(context->range)) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + if (apol_str_appendf(&buf, &buf_sz, "%s:", (context->user != NULL ? context->user : "*")) != 0) { + ERR(p, "%s", strerror(errno)); + goto err_return; + } + if (apol_str_appendf(&buf, &buf_sz, "%s:", (context->role != NULL ? context->role : "*")) != 0) { + ERR(p, "%s", strerror(errno)); + goto err_return; + } + if (apol_str_append(&buf, &buf_sz, (context->type != NULL ? context->type : "*")) != 0) { + ERR(p, "%s", strerror(errno)); + goto err_return; + } + if ((p != NULL && apol_policy_is_mls(p)) || (p == NULL)) { + if (context->range == NULL) { + range_str = strdup("*"); + } else { + range_str = apol_mls_range_render(p, context->range); + } + if (range_str == NULL) { + goto err_return; + } + if (apol_str_appendf(&buf, &buf_sz, ":%s", range_str) != 0) { + ERR(p, "%s", strerror(errno)); + goto err_return; + } + free(range_str); + } + return buf; + + err_return: + free(buf); + free(range_str); + return NULL; +} + +int apol_context_convert(const apol_policy_t * p, apol_context_t * context) +{ + if (p == NULL || context == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if (context->range != NULL) { + return apol_mls_range_convert(p, context->range); + } + return 0; +} diff --git a/libapol/src/domain-trans-analysis-internal.h b/libapol/src/domain-trans-analysis-internal.h new file mode 100644 index 0000000..2c49bed --- /dev/null +++ b/libapol/src/domain-trans-analysis-internal.h @@ -0,0 +1,36 @@ +/** + * @file + * + * Protected routines for domain transition analysis. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_DOMAIN_TRANS_ANALYSIS_INTERNAL_H +#define APOL_DOMAIN_TRANS_ANALYSIS_INTERNAL_H + +/** + * Free all memory associated with a domain transition result, including + * the pointer itself. This function does nothing if the result is NULL. + * @param dtr Pointer to a domain transition result structure to free. + */ +void domain_trans_result_free(void *dtr); + +#endif diff --git a/libapol/src/domain-trans-analysis.c b/libapol/src/domain-trans-analysis.c new file mode 100644 index 0000000..3fef3b8 --- /dev/null +++ b/libapol/src/domain-trans-analysis.c @@ -0,0 +1,2076 @@ +/** + * @file + * + * Routines to perform a domain transition analysis. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" +#include "domain-trans-analysis-internal.h" +#include <apol/domain-trans-analysis.h> +#include <apol/bst.h> + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <stdbool.h> + +/* private data structure definitions */ +struct apol_domain_trans_table +{ + apol_bst_t *domain_table; + apol_bst_t *entrypoint_table; +}; + +typedef struct dom_node +{ + const qpol_type_t *type; + apol_bst_t *process_transition_tree; + apol_bst_t *entrypoint_tree; + apol_vector_t *setexec_rules; +} dom_node_t; + +typedef struct ep_node +{ + const qpol_type_t *type; + apol_bst_t *execute_tree; + apol_bst_t *type_transition_tree; +} ep_node_t; + +typedef struct avrule_node +{ + const qpol_type_t *type; + const qpol_avrule_t *rule; + bool used; +} avrule_node_t; + +typedef struct terule_node +{ + const qpol_type_t *src; + const qpol_type_t *dflt; + const qpol_terule_t *rule; + bool used; +} terule_node_t; + +/* public data structure definitions */ +struct apol_domain_trans_analysis +{ + unsigned char direction; + unsigned char valid; + char *start_type; + char *result; + apol_vector_t *access_types; + apol_vector_t *access_classes; + apol_vector_t *access_perms; + regex_t *result_regex; +}; + +struct apol_domain_trans_result +{ + const qpol_type_t *start_type; + const qpol_type_t *ep_type; + const qpol_type_t *end_type; + apol_vector_t *proc_trans_rules; + apol_vector_t *ep_rules; + apol_vector_t *exec_rules; + apol_vector_t *setexec_rules; + apol_vector_t *type_trans_rules; + bool valid; + /** if access filters used list of rules that satisfy + * the filter criteria (of type qpol_avrule_t) */ + apol_vector_t *access_rules; +}; + +/* private functions */ +/* avrule_node */ +static int avrule_node_cmp(const void *a, const void *b, void *data __attribute__ ((unused))) +{ + const avrule_node_t *an = a; + const avrule_node_t *bn = b; + ssize_t retv = (const char *)an->type - (const char *)bn->type; + if (retv > 0) + return 1; + else if (retv < 0) + return -1; + retv = (const char *)an->rule - (const char *)bn->rule; + if (retv > 0) + return 1; + else if (retv < 0) + return -1; + return 0; +} + +static int avrule_node_reset(void *a, void *b __attribute__ ((unused))) +{ + avrule_node_t *an = a; + if (!a) + return -1; + an->used = false; + return 0; +} + +static avrule_node_t *avrule_node_create(const qpol_type_t * type, const qpol_avrule_t * rule) +{ + avrule_node_t *n = calloc(1, sizeof(*n)); + if (!n) + return NULL; + + n->type = type; + n->rule = rule; + + return n; +} + +/* terule_node */ +static int terule_node_cmp(const void *a, const void *b, void *data __attribute__ ((unused))) +{ + const terule_node_t *an = a; + const terule_node_t *bn = b; + ssize_t retv = (const char *)an->src - (const char *)bn->src; + if (retv > 0) + return 1; + else if (retv < 0) + return -1; + retv = (const char *)an->dflt - (const char *)bn->dflt; + if (retv > 0) + return 1; + else if (retv < 0) + return -1; + retv = (const char *)an->rule - (const char *)bn->rule; + if (retv > 0) + return 1; + else if (retv < 0) + return -1; + return 0; +} + +static int terule_node_reset(void *a, void *b __attribute__ ((unused))) +{ + terule_node_t *an = a; + if (!a) + return -1; + an->used = false; + return 0; +} + +static terule_node_t *terule_node_create(const qpol_type_t * src, const qpol_type_t * dflt, const qpol_terule_t * rule) +{ + terule_node_t *n = calloc(1, sizeof(*n)); + if (!n) + return NULL; + + n->src = src; + n->dflt = dflt; + n->rule = rule; + + return n; +} + +/* dom_node */ +static int dom_node_cmp(const void *a, const void *b, void *data __attribute__ ((unused))) +{ + const dom_node_t *an = a; + const dom_node_t *bn = b; + + if ((const char *)(an->type) < (const char *)(bn->type)) + return -1; + else if ((const char *)(an->type) > (const char *)(bn->type)) + return 1; + return 0; +} + +static void dom_node_free(void *x) +{ + if (!x) + return; + apol_bst_destroy(&(((dom_node_t *) x)->process_transition_tree)); + apol_bst_destroy(&(((dom_node_t *) x)->entrypoint_tree)); + apol_vector_destroy(&(((dom_node_t *) x)->setexec_rules)); + free(x); +} + +static int dom_node_reset(void *a, void *b __attribute__ ((unused))) +{ + dom_node_t *an = a; + if (!a) + return -1; + + if (apol_bst_inorder_map(an->process_transition_tree, avrule_node_reset, NULL) < 0) + return -1; + if (apol_bst_inorder_map(an->entrypoint_tree, avrule_node_reset, NULL) < 0) + return -1; + + return 0; +} + +static dom_node_t *dom_node_create(const qpol_type_t * type) +{ + dom_node_t *n = calloc(1, sizeof(*n)); + if (!n) + return NULL; + + n->type = type; + if (!(n->process_transition_tree = apol_bst_create(avrule_node_cmp, free)) || + !(n->entrypoint_tree = apol_bst_create(avrule_node_cmp, free)) || !(n->setexec_rules = apol_vector_create(NULL))) { + apol_bst_destroy(&n->process_transition_tree); + apol_bst_destroy(&n->entrypoint_tree); + apol_vector_destroy(&n->setexec_rules); + free(n); + return NULL; + } + + return n; +} + +/* ep_node */ +static int ep_node_cmp(const void *a, const void *b, void *data __attribute__ ((unused))) +{ + const ep_node_t *an = a; + const ep_node_t *bn = b; + + if ((const char *)(an->type) < (const char *)(bn->type)) + return -1; + else if ((const char *)(an->type) > (const char *)(bn->type)) + return 1; + return 0; +} + +static void ep_node_free(void *x) +{ + if (!x) + return; + apol_bst_destroy(&(((ep_node_t *) x)->type_transition_tree)); + apol_bst_destroy(&(((ep_node_t *) x)->execute_tree)); + free(x); +} + +static int ep_node_reset(void *a, void *b __attribute__ ((unused))) +{ + ep_node_t *an = a; + if (!a) + return -1; + + if (apol_bst_inorder_map(an->execute_tree, avrule_node_reset, NULL) < 0) + return -1; + if (apol_bst_inorder_map(an->type_transition_tree, terule_node_reset, NULL) < 0) + return -1; + return 0; +} + +static ep_node_t *ep_node_create(const qpol_type_t * type) +{ + ep_node_t *n = calloc(1, sizeof(*n)); + if (!n) + return NULL; + + n->type = type; + if (!(n->execute_tree = apol_bst_create(avrule_node_cmp, free)) || + !(n->type_transition_tree = apol_bst_create(terule_node_cmp, free))) { + apol_bst_destroy(&n->execute_tree); + apol_bst_destroy(&n->type_transition_tree); + free(n); + return NULL; + } + + return n; +} + +/* table */ +static apol_domain_trans_table_t *apol_domain_trans_table_new(apol_policy_t * policy) +{ + apol_domain_trans_table_t *new_table = NULL; + int error; + + if (!policy) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + + new_table = (apol_domain_trans_table_t *) calloc(1, sizeof(apol_domain_trans_table_t)); + if (!new_table) { + ERR(policy, "%s", strerror(ENOMEM)); + error = ENOMEM; + goto cleanup; + } + + if (!(new_table->domain_table = apol_bst_create(dom_node_cmp, dom_node_free))) { + ERR(policy, "%s", strerror(ENOMEM)); + error = ENOMEM; + goto cleanup; + } + if (!(new_table->entrypoint_table = apol_bst_create(ep_node_cmp, ep_node_free))) { + ERR(policy, "%s", strerror(ENOMEM)); + error = ENOMEM; + goto cleanup; + } + + return new_table; + cleanup: + domain_trans_table_destroy(&new_table); + errno = error; + return NULL; +} + +static int table_add_avrule(apol_policy_t * policy, apol_domain_trans_table_t * dta_table, const qpol_avrule_t * rule) +{ + qpol_policy_t *qp = apol_policy_get_qpol(policy); + const qpol_type_t *src; + const qpol_type_t *tgt; + qpol_avrule_get_source_type(qp, rule, &src); + qpol_avrule_get_target_type(qp, rule, &tgt); + apol_vector_t *sources = apol_query_expand_type(policy, src); + apol_vector_t *targets = apol_query_expand_type(policy, tgt); + bool exec = false, ep = false, proc_trans = false, setexec = false; + qpol_iterator_t *iter = NULL; + int error = 0; + qpol_avrule_get_perm_iter(qp, rule, &iter); + if (!iter || !sources || !targets) { + error = errno; + goto err; + } + + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + void *x; + qpol_iterator_get_item(iter, &x); + char *perm = x; + if (!strcmp("execute", perm)) + exec = true; + if (!strcmp("entrypoint", perm)) + ep = true; + if (!strcmp("transition", perm)) + proc_trans = true; + if (!strcmp("setexec", perm)) + setexec = true; + free(x); + } + qpol_iterator_destroy(&iter); + + if (proc_trans || ep || setexec) { + for (size_t i = 0; i < apol_vector_get_size(sources); i++) { + dom_node_t *dnode = NULL; + dom_node_t dummy = { apol_vector_get_element(sources, i), NULL, NULL, NULL }; + if (apol_bst_get_element(dta_table->domain_table, &dummy, NULL, (void **)&dnode)) { + dom_node_t *new_dnode = NULL; + if (!(new_dnode = dom_node_create(dummy.type)) || + apol_bst_insert(dta_table->domain_table, (void *)new_dnode, NULL)) { + error = errno; + dom_node_free(new_dnode); + goto err; + } + dnode = new_dnode; + } + if (setexec) { + if (apol_vector_append_unique(dnode->setexec_rules, (void *)rule, NULL, NULL)) { + error = errno; + goto err; + } + } + for (size_t j = 0; j < apol_vector_get_size(targets); j++) { + if (proc_trans) { + avrule_node_t *new_node = + avrule_node_create((const qpol_type_t *)apol_vector_get_element(targets, j), rule); + if (!new_node || + apol_bst_insert_and_get(dnode->process_transition_tree, (void **)&new_node, NULL) < 0) { + error = errno; + free(new_node); + goto err; + } + } + if (ep) { + avrule_node_t *new_node = + avrule_node_create((const qpol_type_t *)apol_vector_get_element(targets, j), rule); + if (!new_node || + apol_bst_insert_and_get(dnode->entrypoint_tree, (void **)&new_node, NULL) < 0) { + error = errno; + free(new_node); + goto err; + } + } + } + } + } + if (exec) { + for (size_t i = 0; i < apol_vector_get_size(targets); i++) { + ep_node_t *enode = NULL; + ep_node_t dummy = { apol_vector_get_element(targets, i), NULL, NULL }; + if (apol_bst_get_element(dta_table->entrypoint_table, &dummy, NULL, (void **)&enode)) { + ep_node_t *new_enode = NULL; + if (!(new_enode = ep_node_create(dummy.type)) || + apol_bst_insert(dta_table->entrypoint_table, (void *)new_enode, NULL)) { + error = errno; + ep_node_free(new_enode); + goto err; + } + enode = new_enode; + } + for (size_t j = 0; j < apol_vector_get_size(sources); j++) { + avrule_node_t *new_node = + avrule_node_create((const qpol_type_t *)apol_vector_get_element(sources, j), rule); + if (!new_node || apol_bst_insert_and_get(enode->execute_tree, (void **)&new_node, NULL) < 0) { + error = errno; + free(new_node); + goto err; + } + } + } + } + + apol_vector_destroy(&sources); + apol_vector_destroy(&targets); + return 0; + + err: + qpol_iterator_destroy(&iter); + apol_vector_destroy(&sources); + apol_vector_destroy(&targets); + errno = error; + return -1; +} + +static int table_add_terule(apol_policy_t * policy, apol_domain_trans_table_t * dta_table, const qpol_terule_t * rule) +{ + qpol_policy_t *qp = apol_policy_get_qpol(policy); + const qpol_type_t *src; + const qpol_type_t *tgt; + const qpol_type_t *dflt; + qpol_terule_get_source_type(qp, rule, &src); + qpol_terule_get_target_type(qp, rule, &tgt); + qpol_terule_get_default_type(qp, rule, &dflt); + apol_vector_t *sources = apol_query_expand_type(policy, src); + apol_vector_t *targets = apol_query_expand_type(policy, tgt); + int error = 0; + for (size_t i = 0; i < apol_vector_get_size(targets); i++) { + ep_node_t *enode = NULL; + ep_node_t dummy = { apol_vector_get_element(targets, i), NULL, NULL }; + if (apol_bst_get_element(dta_table->entrypoint_table, &dummy, NULL, (void **)&enode)) { + ep_node_t *new_enode = NULL; + if (!(new_enode = ep_node_create(dummy.type)) || + apol_bst_insert(dta_table->entrypoint_table, (void *)new_enode, NULL)) { + error = errno; + ep_node_free(new_enode); + goto err; + } + enode = new_enode; + } + for (size_t j = 0; j < apol_vector_get_size(sources); j++) { + terule_node_t *new_node = + terule_node_create((const qpol_type_t *)apol_vector_get_element(sources, j), dflt, rule); + if (apol_bst_insert_and_get(enode->type_transition_tree, (void **)&new_node, NULL) < 0) { + error = errno; + free(new_node); + goto err; + } + } + } + + apol_vector_destroy(&sources); + apol_vector_destroy(&targets); + return 0; + err: + apol_vector_destroy(&sources); + apol_vector_destroy(&targets); + errno = error; + return -1; +} + +/* result */ +apol_domain_trans_result_t *domain_trans_result_create() +{ + apol_domain_trans_result_t *res = calloc(1, sizeof(*res)); + if (!res) + return NULL; + + int error = 0; + if (!(res->proc_trans_rules = apol_vector_create(NULL)) || !(res->ep_rules = apol_vector_create(NULL)) || + !(res->exec_rules = apol_vector_create(NULL)) || !(res->setexec_rules = apol_vector_create(NULL)) || + !(res->type_trans_rules = apol_vector_create(NULL))) { + error = errno; + goto err; + } + + return res; + err: + apol_domain_trans_result_destroy(&res); + errno = error; + return NULL; +} + +/* public functions */ +/* table */ +int apol_policy_build_domain_trans_table(apol_policy_t * policy) +{ + int error = 0; + apol_avrule_query_t *avq = NULL; + apol_terule_query_t *teq = NULL; + apol_vector_t *avrules = NULL; + apol_vector_t *terules = NULL; + + if (!policy) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + if (policy->domain_trans_table) { + return 0; /* already built */ + } + + apol_domain_trans_table_t *dta_table = policy->domain_trans_table = apol_domain_trans_table_new(policy); + if (!policy->domain_trans_table) { + error = errno; + goto err; + } + + avq = apol_avrule_query_create(); + apol_avrule_query_set_rules(policy, avq, QPOL_RULE_ALLOW); + apol_avrule_query_append_class(policy, avq, "file"); + apol_avrule_query_append_class(policy, avq, "process"); + apol_avrule_query_append_perm(policy, avq, "execute"); + apol_avrule_query_append_perm(policy, avq, "entrypoint"); + apol_avrule_query_append_perm(policy, avq, "transition"); + apol_avrule_query_append_perm(policy, avq, "setexec"); + if (apol_avrule_get_by_query(policy, avq, &avrules)) { + error = errno; + goto err; + } + apol_avrule_query_destroy(&avq); + for (size_t i = 0; i < apol_vector_get_size(avrules); i++) { + if (table_add_avrule(policy, dta_table, (const qpol_avrule_t *)apol_vector_get_element(avrules, i))) { + error = errno; + goto err; + } + } + apol_vector_destroy(&avrules); + + teq = apol_terule_query_create(); + apol_terule_query_set_rules(policy, teq, QPOL_RULE_TYPE_TRANS); + apol_terule_query_append_class(policy, teq, "process"); + if (apol_terule_get_by_query(policy, teq, &terules)) { + error = errno; + goto err; + } + apol_terule_query_destroy(&teq); + for (size_t i = 0; i < apol_vector_get_size(terules); i++) { + if (table_add_terule(policy, dta_table, (const qpol_terule_t *)apol_vector_get_element(terules, i))) { + error = errno; + goto err; + } + } + apol_vector_destroy(&terules); + + return 0; + + err: + apol_avrule_query_destroy(&avq); + apol_vector_destroy(&avrules); + apol_terule_query_destroy(&teq); + apol_vector_destroy(&terules); + domain_trans_table_destroy(&dta_table); + policy->domain_trans_table = NULL; + errno = error; + return -1; +} + +int apol_policy_domain_trans_table_build(apol_policy_t * policy) +{ + return apol_policy_build_domain_trans_table(policy); +} + +void domain_trans_table_destroy(apol_domain_trans_table_t ** table) +{ + if (!table || !(*table)) + return; + + apol_bst_destroy(&(*table)->domain_table); + apol_bst_destroy(&(*table)->entrypoint_table); + free(*table); + *table = NULL; +} + +void apol_policy_reset_domain_trans_table(apol_policy_t * policy) +{ + if (!policy || !policy->domain_trans_table) + return; + apol_bst_inorder_map(policy->domain_trans_table->domain_table, dom_node_reset, NULL); + apol_bst_inorder_map(policy->domain_trans_table->entrypoint_table, ep_node_reset, NULL); + return; +} + +void apol_domain_trans_table_reset(apol_policy_t * policy) +{ + apol_policy_reset_domain_trans_table(policy); +} + +/* analysis */ +apol_domain_trans_analysis_t *apol_domain_trans_analysis_create(void) +{ + apol_domain_trans_analysis_t *new_dta = NULL; + int error = 0; + + if (!(new_dta = calloc(1, sizeof(apol_domain_trans_analysis_t)))) { + error = errno; + goto err; + } + + new_dta->valid = APOL_DOMAIN_TRANS_SEARCH_VALID; /* by default search only valid transitions */ + + return new_dta; + + err: + apol_domain_trans_analysis_destroy(&new_dta); + errno = error; + return NULL; +} + +void apol_domain_trans_analysis_destroy(apol_domain_trans_analysis_t ** dta) +{ + if (!dta || !(*dta)) + return; + + free((*dta)->start_type); + free((*dta)->result); + apol_vector_destroy(&((*dta)->access_types)); + apol_vector_destroy(&((*dta)->access_classes)); + apol_vector_destroy(&((*dta)->access_perms)); + apol_regex_destroy(&((*dta)->result_regex)); + free(*dta); + *dta = NULL; +} + +int apol_domain_trans_analysis_set_direction(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + unsigned char direction) +{ + if (!dta || (direction != APOL_DOMAIN_TRANS_DIRECTION_FORWARD && direction != APOL_DOMAIN_TRANS_DIRECTION_REVERSE)) { + ERR(policy, "Error setting analysis direction: %s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + dta->direction = direction; + + return 0; +} + +int apol_domain_trans_analysis_set_valid(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, unsigned char valid) +{ + if (!dta || valid & ~(APOL_DOMAIN_TRANS_SEARCH_BOTH)) { + ERR(policy, "Error setting analysis validity flag: %s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + dta->valid = valid; + + return 0; +} + +int apol_domain_trans_analysis_set_start_type(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + const char *type_name) +{ + char *tmp = NULL; + int error = 0; + + if (!dta || !type_name) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + if (!(tmp = strdup(type_name))) { + error = errno; + ERR(policy, "%s", strerror(error)); + errno = error; + return -1; + } + + free(dta->start_type); + dta->start_type = tmp; + + return 0; +} + +int apol_domain_trans_analysis_set_result_regex(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, const char *regex) +{ + if (!dta) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + if (!regex) { + apol_regex_destroy(&dta->result_regex); + return 0; + } + + return apol_query_set(policy, &dta->result, &dta->result_regex, regex); +} + +int apol_domain_trans_analysis_append_access_type(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + const char *type_name) +{ + char *tmp = NULL; + int error = 0; + + if (!dta) { + ERR(policy, "Error appending type to analysis: %s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + if (!type_name) { + apol_vector_destroy(&dta->access_types); + return 0; + } + + if (!dta->access_types) { + if (!(dta->access_types = apol_vector_create(free))) { + error = errno; + ERR(policy, "%s", strerror(error)); + errno = error; + return -1; + } + } + + if (!(tmp = strdup(type_name))) { + error = errno; + ERR(policy, "%s", strerror(error)); + errno = error; + return -1; + } + + if (apol_vector_append(dta->access_types, tmp)) { + error = errno; + free(tmp); + ERR(policy, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +int apol_domain_trans_analysis_append_class_perm(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + const char *class_name, const char *perm_name) +{ + if (apol_domain_trans_analysis_append_class(policy, dta, class_name)) + return -1; + return apol_domain_trans_analysis_append_perm(policy, dta, perm_name); +} + +int apol_domain_trans_analysis_append_class(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + const char *class_name) +{ + char *tmp = NULL; + int error = 0; + + if (!dta) { + ERR(policy, "Error appending class to analysis: %s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + if (!class_name) { + apol_vector_destroy(&dta->access_classes); + return 0; + } + + if (!dta->access_classes) { + if (!(dta->access_classes = apol_vector_create(free))) { + error = errno; + ERR(policy, "%s", strerror(error)); + errno = error; + return -1; + } + } + + if (!(tmp = strdup(class_name))) { + error = errno; + ERR(policy, "%s", strerror(error)); + errno = error; + return -1; + } + + if (apol_vector_append(dta->access_classes, tmp)) { + error = errno; + free(tmp); + ERR(policy, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +int apol_domain_trans_analysis_append_perm(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, const char *perm_name) +{ + char *tmp = NULL; + int error = 0; + + if (!dta) { + ERR(policy, "Error appending perm to analysis: %s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + if (!perm_name) { + apol_vector_destroy(&dta->access_perms); + return 0; + } + + if (!dta->access_perms) { + if (!(dta->access_perms = apol_vector_create(free))) { + error = errno; + ERR(policy, "%s", strerror(error)); + errno = error; + return -1; + } + } + + if (!(tmp = strdup(perm_name))) { + error = errno; + ERR(policy, "%s", strerror(error)); + errno = error; + return -1; + } + + if (apol_vector_append(dta->access_perms, tmp)) { + error = errno; + free(tmp); + ERR(policy, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +static bool requires_setexec_or_type_trans(apol_policy_t * policy) +{ + const qpol_policy_t *qp = apol_policy_get_qpol(policy); + unsigned int policy_version = 0; + qpol_policy_get_policy_version(qp, &policy_version); + int is_modular = qpol_policy_has_capability(policy->p, QPOL_CAP_MODULES); + return (policy_version >= 15 || is_modular); +} + +struct rule_map_data +{ + const qpol_type_t *search; + const qpol_type_t *dflt; + apol_vector_t *node_list; + bool is_avnode; +}; + +static int node_list_map_fn(void *node, void *data) +{ + struct rule_map_data *rm = data; + if (rm->is_avnode) { + avrule_node_t *anode = node; + if (anode->type == rm->search && !anode->used) + if (apol_vector_append(rm->node_list, node)) + return -1; + return 0; + } else { + terule_node_t *tnode = node; + if ((!rm->search || (rm->search == tnode->src)) && (!rm->dflt || (rm->dflt == tnode->dflt)) && + rm->search != rm->dflt && !tnode->used) + if (apol_vector_append(rm->node_list, node)) + return -1; + return 0; + } +} + +static apol_vector_t *find_avrules_in_node(void *node, unsigned int rule_type, const qpol_type_t * search) +{ + int error = 0; + apol_vector_t *rule_nodes = apol_vector_create(NULL); //shallow copies only + struct rule_map_data data = { search, NULL, rule_nodes, true }; + switch (rule_type) { + case APOL_DOMAIN_TRANS_RULE_PROC_TRANS: + { + dom_node_t *dnode = node; + if (apol_bst_inorder_map(dnode->process_transition_tree, node_list_map_fn, (void *)&data) < 0) { + error = errno; + goto err; + } + break; + } + case APOL_DOMAIN_TRANS_RULE_ENTRYPOINT: + { + dom_node_t *dnode = node; + if (apol_bst_inorder_map(dnode->entrypoint_tree, node_list_map_fn, (void *)&data) < 0) { + error = errno; + goto err; + } + break; + } + case APOL_DOMAIN_TRANS_RULE_EXEC: + { + ep_node_t *enode = node; + if (apol_bst_inorder_map(enode->execute_tree, node_list_map_fn, (void *)&data) < 0) { + error = errno; + goto err; + } + break; + } + default: + { + error = EINVAL; + goto err; + } + } + + return rule_nodes; + + err: + apol_vector_destroy(&rule_nodes); + errno = error; + return NULL; +} + +static apol_vector_t *find_terules_in_node(ep_node_t * node, const qpol_type_t * search, const qpol_type_t * dflt) +{ + int error = 0; + apol_vector_t *rule_nodes = apol_vector_create(NULL); //shallow copies only + struct rule_map_data data = { search, dflt, rule_nodes, false }; + if (apol_bst_inorder_map(node->type_transition_tree, node_list_map_fn, (void *)&data) < 0) { + error = errno; + goto err; + } + + return rule_nodes; + + err: + apol_vector_destroy(&rule_nodes); + errno = error; + return NULL; +} + +static apol_domain_trans_result_t *find_result(apol_vector_t * local_results, const qpol_type_t * src, const qpol_type_t * tgt, + const qpol_type_t * dflt) +{ + for (size_t i = 0; i < apol_vector_get_size(local_results); i++) { + apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i); + if (res->start_type == src && res->end_type == dflt && res->ep_type == tgt) + return res; + } + return NULL; +} + +static int domain_trans_table_find_orphan_type_transitions(apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + apol_vector_t * local_results) +{ + int error = 0; + const qpol_type_t *search = NULL; + qpol_policy_get_type_by_name(apol_policy_get_qpol(policy), dta->start_type, &search); + apol_domain_trans_result_t *tmp_result = NULL; + //walk ep table + apol_vector_t *epnodes = apol_bst_get_vector(policy->domain_trans_table->entrypoint_table, 0); + if (!epnodes) + return -1; + for (size_t i = 0; i < apol_vector_get_size(epnodes); i++) { + ep_node_t *node = apol_vector_get_element(epnodes, i); + //find any unused type transitions + apol_vector_t *ttnodes = NULL; + if (dta->direction == APOL_DOMAIN_TRANS_DIRECTION_FORWARD) + ttnodes = find_terules_in_node(node, search, NULL); + else + ttnodes = find_terules_in_node(node, NULL, search); + for (size_t j = 0; j < apol_vector_get_size(ttnodes); j++) { + bool add = false; + terule_node_t *tn = apol_vector_get_element(ttnodes, j); + tn->used = true; + //if missing an entrypoint rule this transition may have already been added to the results + tmp_result = find_result(local_results, tn->src, node->type, tn->dflt); + if (!tmp_result) { + add = true; + tmp_result = domain_trans_result_create(); + } + if (!tmp_result) { + error = errno; + apol_vector_destroy(&ttnodes); + goto err; + } + tmp_result->start_type = tn->src; + tmp_result->end_type = tn->dflt; + tmp_result->ep_type = node->type; + //check for exec + apol_vector_t *execrules = + find_avrules_in_node((void *)node, APOL_DOMAIN_TRANS_RULE_EXEC, tmp_result->start_type); + for (size_t k = 0; k < apol_vector_get_size(execrules); k++) { + avrule_node_t *n = apol_vector_get_element(execrules, k); + if (apol_vector_append(tmp_result->exec_rules, (void *)n->rule)) { + error = errno; + apol_vector_destroy(&execrules); + if (!add) + tmp_result = NULL; + goto err; + } + } + apol_vector_destroy(&execrules); + //check for proc_trans and setexec + dom_node_t dummy = { tmp_result->start_type, NULL, NULL, NULL }; + dom_node_t *start_node = NULL; + apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&dummy, NULL, (void **)&start_node); + if (start_node) { + //only copy setexec_rules if a new result will be added + if (add && apol_vector_get_size(start_node->setexec_rules)) { + if (apol_vector_cat(tmp_result->setexec_rules, start_node->setexec_rules)) { + error = errno; + goto err; + } + } + //add any unused proc_trans rules + apol_vector_t *proc_trans_rules = + find_avrules_in_node((void *)start_node, APOL_DOMAIN_TRANS_RULE_PROC_TRANS, + tmp_result->end_type); + for (size_t k = 0; k < apol_vector_get_size(proc_trans_rules); k++) { + avrule_node_t *avr = apol_vector_get_element(proc_trans_rules, k); + if (apol_vector_append(tmp_result->proc_trans_rules, (void *)avr->rule)) { + error = errno; + if (!add) + tmp_result = NULL; + apol_vector_destroy(&proc_trans_rules); + goto err; + } + } + apol_vector_destroy(&proc_trans_rules); + apol_vector_sort_uniquify(tmp_result->proc_trans_rules, NULL, NULL); + } + if (add) { + if (apol_vector_append(local_results, (void *)tmp_result)) { + error = errno; + goto err; + } + } + tmp_result = NULL; + } + apol_vector_destroy(&ttnodes); + } + apol_vector_destroy(&epnodes); + + return 0; + + err: + apol_vector_destroy(&epnodes); + apol_domain_trans_result_destroy(&tmp_result); + errno = error; + return -1; +} + +static int domain_trans_table_get_all_forward_trans(apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + apol_vector_t * local_results, const qpol_type_t * start_type) +{ + int error = 0; + //create template result this will hold common data for each step and be copied as needed + apol_domain_trans_result_t *tmpl_result = domain_trans_result_create(); + if (!tmpl_result) { + error = errno; + goto err; + } + //find start node + dom_node_t dummy = { start_type, NULL, NULL, NULL }; + dom_node_t *start_node = NULL; + apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&dummy, NULL, (void **)&start_node); + if (start_node) { + tmpl_result->start_type = start_type; + //if needed and present record setexec + if (requires_setexec_or_type_trans(policy) && apol_vector_get_size(start_node->setexec_rules)) { + if (apol_vector_cat(tmpl_result->setexec_rules, start_node->setexec_rules)) { + error = errno; + goto err; + } + } + //check all proc trans to build list of end types + apol_vector_t *proc_trans_rules = apol_bst_get_vector(start_node->process_transition_tree, 0); + apol_vector_t *potential_end_types = apol_vector_create(NULL); + for (size_t i = 0; i < apol_vector_get_size(proc_trans_rules); i++) { + avrule_node_t *ptnode = apol_vector_get_element(proc_trans_rules, i); + apol_vector_append(potential_end_types, (void *)ptnode->type); + } + apol_vector_destroy(&proc_trans_rules); + apol_vector_sort_uniquify(potential_end_types, NULL, NULL); + //for each end check ep + for (size_t i = 0; i < apol_vector_get_size(potential_end_types); i++) { + dummy.type = tmpl_result->end_type = apol_vector_get_element(potential_end_types, i); + dom_node_t *end_node = NULL; + apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&dummy, NULL, (void **)&end_node); + const qpol_type_t *end_type = dummy.type; + if (end_type == start_type) + continue; + //get all proc trans rules for ths end (may be multiple due to attributes) + apol_vector_t *ptrules = + find_avrules_in_node((void *)start_node, APOL_DOMAIN_TRANS_RULE_PROC_TRANS, end_type); + apol_vector_destroy(&tmpl_result->proc_trans_rules); + tmpl_result->proc_trans_rules = apol_vector_create(NULL); + for (size_t j = 0; j < apol_vector_get_size(ptrules); j++) { + avrule_node_t *pt_ent = apol_vector_get_element(ptrules, j); + pt_ent->used = true; + if (apol_vector_append(tmpl_result->proc_trans_rules, (void *)pt_ent->rule)) { + error = errno; + apol_vector_destroy(&ptrules); + apol_vector_destroy(&potential_end_types); + goto err; + } + } + apol_vector_destroy(&ptrules); + apol_vector_sort_uniquify(tmpl_result->proc_trans_rules, NULL, NULL); + if (end_node) { + //collect potential entrypoint types + apol_vector_t *eprules = apol_bst_get_vector(end_node->entrypoint_tree, 0); + apol_vector_t *potential_ep_types = apol_vector_create(NULL); + if (!eprules || !potential_ep_types) { + error = errno; + apol_vector_destroy(&eprules); + apol_vector_destroy(&potential_end_types); + goto err; + } + for (size_t j = 0; j < apol_vector_get_size(eprules); j++) { + avrule_node_t *epr = apol_vector_get_element(eprules, j); + if (apol_vector_append(potential_ep_types, (void *)epr->type)) { + error = errno; + apol_vector_destroy(&eprules); + apol_vector_destroy(&potential_end_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } + apol_vector_destroy(&eprules); + apol_vector_sort_uniquify(potential_ep_types, NULL, NULL); + //for each ep find exec by start + for (size_t j = 0; j < apol_vector_get_size(potential_ep_types); j++) { + tmpl_result->ep_type = apol_vector_get_element(potential_ep_types, j); + ep_node_t edummy = + { (const qpol_type_t *)apol_vector_get_element(potential_ep_types, j), NULL, NULL }; + ep_node_t *epnode = NULL; + apol_bst_get_element(policy->domain_trans_table->entrypoint_table, (void *)&edummy, NULL, + (void **)&epnode); + //get all entrypoint rules for ths end (may be multiple due to attributes) + apol_vector_destroy(&tmpl_result->ep_rules); + tmpl_result->ep_rules = apol_vector_create(NULL); + if (!tmpl_result->ep_rules) { + error = errno; + apol_vector_destroy(&potential_end_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + eprules = find_avrules_in_node((void *)end_node, APOL_DOMAIN_TRANS_RULE_ENTRYPOINT, + tmpl_result->ep_type); + for (size_t k = 0; k < apol_vector_get_size(eprules); k++) { + avrule_node_t *ep_ent = apol_vector_get_element(eprules, k); + ep_ent->used = true; + if (apol_vector_append(tmpl_result->ep_rules, (void *)ep_ent->rule)) { + error = errno; + apol_vector_destroy(&eprules); + apol_vector_destroy(&potential_end_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } + apol_vector_destroy(&eprules); + apol_vector_sort_uniquify(tmpl_result->ep_rules, NULL, NULL); + if (epnode) { + //if present find tt + apol_vector_destroy(&tmpl_result->type_trans_rules); + tmpl_result->type_trans_rules = apol_vector_create(NULL); + if (!tmpl_result->type_trans_rules) { + error = errno; + apol_vector_destroy(&potential_end_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + apol_vector_t *ttrules = find_terules_in_node(epnode, start_type, end_type); + for (size_t l = 0; l < apol_vector_get_size(ttrules); l++) { + terule_node_t *tn = apol_vector_get_element(ttrules, l); + if (apol_vector_append(tmpl_result->type_trans_rules, (void *)tn->rule)) { + error = errno; + apol_vector_destroy(&ttrules); + apol_vector_destroy(&potential_end_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } + apol_vector_destroy(&ttrules); + apol_vector_sort_uniquify(tmpl_result->type_trans_rules, NULL, NULL); + //find execute rules + apol_vector_destroy(&tmpl_result->exec_rules); + tmpl_result->exec_rules = apol_vector_create(NULL); + if (!tmpl_result->exec_rules) { + error = errno; + apol_vector_destroy(&potential_end_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + apol_vector_t *execrules = + find_avrules_in_node(epnode, APOL_DOMAIN_TRANS_RULE_EXEC, start_type); + if (apol_vector_get_size(execrules)) { + for (size_t l = 0; l < apol_vector_get_size(execrules); l++) { + avrule_node_t *xnode = apol_vector_get_element(execrules, l); + //do not mark xnode as used here; it is valid to re-use it. + if (apol_vector_append + (tmpl_result->exec_rules, (void *)xnode->rule)) { + error = errno; + apol_vector_destroy(&execrules); + apol_vector_destroy(&potential_end_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } + apol_vector_destroy(&execrules); + apol_vector_sort_uniquify(tmpl_result->exec_rules, NULL, NULL); + //found everything possible add a result + apol_domain_trans_result_t *tmp = + apol_domain_trans_result_create_from_domain_trans_result + (tmpl_result); + if (!tmp || apol_vector_append(local_results, (void *)tmp)) { + error = errno; + apol_domain_trans_result_destroy(&tmp); + apol_vector_destroy(&potential_end_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + //reset execute rules + apol_vector_destroy(&tmpl_result->exec_rules); + tmpl_result->exec_rules = apol_vector_create(NULL); + if (!tmpl_result->exec_rules) { + error = errno; + apol_vector_destroy(&potential_end_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + //reset type transition rules + apol_vector_destroy(&tmpl_result->type_trans_rules); + tmpl_result->type_trans_rules = apol_vector_create(NULL); + if (!tmpl_result->type_trans_rules) { + error = errno; + apol_vector_destroy(&potential_end_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } else { + //have proc_trans and entrypoint but no execute + apol_domain_trans_result_t *tmp = + apol_domain_trans_result_create_from_domain_trans_result + (tmpl_result); + if (!tmp || apol_vector_append(local_results, (void *)tmp)) { + error = errno; + apol_domain_trans_result_destroy(&tmp); + apol_vector_destroy(&potential_end_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } + apol_vector_destroy(&execrules); + } else { + //have proc_trans and entrypoint but no execute + apol_domain_trans_result_t *tmp = + apol_domain_trans_result_create_from_domain_trans_result(tmpl_result); + if (!tmp || apol_vector_append(local_results, (void *)tmp)) { + error = errno; + apol_domain_trans_result_destroy(&tmp); + apol_vector_destroy(&potential_end_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } + //reset entrypoint rules + apol_vector_destroy(&tmpl_result->ep_rules); + tmpl_result->ep_rules = apol_vector_create(NULL); + if (!tmpl_result->ep_rules) { + error = errno; + apol_vector_destroy(&potential_end_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } + apol_vector_destroy(&potential_ep_types); + } else { + //have proc_trans but end has no ep + apol_domain_trans_result_t *tmp = + apol_domain_trans_result_create_from_domain_trans_result(tmpl_result); + if (!tmp || apol_vector_append(local_results, (void *)tmp)) { + error = errno; + apol_domain_trans_result_destroy(&tmp); + goto err; + } + } + } + apol_vector_destroy(&potential_end_types); + //validate all + for (size_t i = 0; i < apol_vector_get_size(local_results); i++) { + apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i); + if (res->start_type && res->ep_type && res->end_type && apol_vector_get_size(res->proc_trans_rules) && + apol_vector_get_size(res->ep_rules) && apol_vector_get_size(res->exec_rules) && + (requires_setexec_or_type_trans(policy) + ? (apol_vector_get_size(res->setexec_rules) || apol_vector_get_size(res->type_trans_rules)) : true)) { + res->valid = true; + } + } + } + //iff looking for invalid find orphan type_transition rules + if (dta->valid & APOL_DOMAIN_TRANS_SEARCH_INVALID) { + if (domain_trans_table_find_orphan_type_transitions(policy, dta, local_results)) { + error = errno; + goto err; + } + } + apol_domain_trans_result_destroy(&tmpl_result); + + return 0; + err: + apol_domain_trans_result_destroy(&tmpl_result); + errno = error; + return -1; +} + +static int domain_trans_table_get_all_reverse_trans(apol_policy_t * policy, apol_domain_trans_analysis_t * dta, + apol_vector_t * local_results, const qpol_type_t * end_type) +{ + int error = 0; + //create template result this will hold common data for each step and be copied as needed + apol_domain_trans_result_t *tmpl_result = domain_trans_result_create(); + if (!tmpl_result) { + error = errno; + goto err; + } + //find end node + dom_node_t dummy = { end_type, NULL, NULL, NULL }; + dom_node_t *end_node = NULL; + apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&dummy, NULL, (void **)&end_node); + if (end_node) { + tmpl_result->end_type = end_type; + //collect potential entrypoint types + apol_vector_t *eprules = apol_bst_get_vector(end_node->entrypoint_tree, 0); + apol_vector_t *potential_ep_types = apol_vector_create(NULL); + if (!eprules || !potential_ep_types) { + error = errno; + apol_vector_destroy(&eprules); + goto err; + } + for (size_t j = 0; j < apol_vector_get_size(eprules); j++) { + avrule_node_t *epr = apol_vector_get_element(eprules, j); + if (apol_vector_append(potential_ep_types, (void *)epr->type)) { + error = errno; + apol_vector_destroy(&eprules); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } + apol_vector_destroy(&eprules); + apol_vector_sort_uniquify(potential_ep_types, NULL, NULL); + for (size_t i = 0; i < apol_vector_get_size(potential_ep_types); i++) { + tmpl_result->ep_type = apol_vector_get_element(potential_ep_types, i); + //get all ep rules for this end (may be multiple due to attributes) + eprules = find_avrules_in_node((void *)end_node, APOL_DOMAIN_TRANS_RULE_ENTRYPOINT, tmpl_result->ep_type); + apol_vector_destroy(&tmpl_result->ep_rules); + tmpl_result->ep_rules = apol_vector_create(NULL); + for (size_t j = 0; j < apol_vector_get_size(eprules); j++) { + avrule_node_t *ep_ent = apol_vector_get_element(eprules, j); + ep_ent->used = true; + if (apol_vector_append(tmpl_result->ep_rules, (void *)ep_ent->rule)) { + error = errno; + apol_vector_destroy(&eprules); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } + apol_vector_destroy(&eprules); + apol_vector_sort_uniquify(tmpl_result->ep_rules, NULL, NULL); + ep_node_t edummy = { tmpl_result->ep_type, NULL, NULL }; + ep_node_t *epnode = NULL; + apol_bst_get_element(policy->domain_trans_table->entrypoint_table, (void *)&edummy, NULL, (void **)&epnode); + //for each ep find exec rules to generate list of potential start types + if (epnode) { + apol_vector_t *execrules = apol_bst_get_vector(epnode->execute_tree, 0); + apol_vector_t *potential_start_types = apol_vector_create(NULL); + if (!execrules || !potential_start_types) { + error = errno; + apol_vector_destroy(&execrules); + goto err; + } + for (size_t k = 0; k < apol_vector_get_size(execrules); k++) { + avrule_node_t *n = apol_vector_get_element(execrules, k); + if (apol_vector_append(potential_start_types, (void *)n->type)) { + error = errno; + apol_vector_destroy(&execrules); + apol_vector_destroy(&potential_start_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } + apol_vector_destroy(&execrules); + apol_vector_sort_uniquify(potential_start_types, NULL, NULL); + for (size_t k = 0; k < apol_vector_get_size(potential_start_types); k++) { + tmpl_result->start_type = apol_vector_get_element(potential_start_types, k); + //no transition to self + if (tmpl_result->end_type == tmpl_result->start_type) + continue; + //get all execute rule for this start type + apol_vector_t *exec_rules = + find_avrules_in_node((void *)epnode, APOL_DOMAIN_TRANS_RULE_EXEC, + tmpl_result->start_type); + apol_vector_destroy(&tmpl_result->exec_rules); + tmpl_result->exec_rules = apol_vector_create(NULL); + for (size_t l = 0; l < apol_vector_get_size(exec_rules); l++) { + avrule_node_t *n = apol_vector_get_element(exec_rules, l); + n->used = true; + if (apol_vector_append(tmpl_result->exec_rules, (void *)n->rule)) { + error = errno; + apol_vector_destroy(&exec_rules); + apol_vector_destroy(&potential_start_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } + apol_vector_destroy(&exec_rules); + apol_vector_sort_uniquify(tmpl_result->exec_rules, NULL, NULL); + //check for type transition rules + apol_vector_t *ttrules = + find_terules_in_node(epnode, tmpl_result->start_type, tmpl_result->end_type); + apol_vector_destroy(&tmpl_result->type_trans_rules); + tmpl_result->type_trans_rules = apol_vector_create(NULL); + if (!tmpl_result->type_trans_rules) { + error = errno; + apol_vector_destroy(&ttrules); + apol_vector_destroy(&potential_start_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + for (size_t l = 0; l < apol_vector_get_size(ttrules); l++) { + terule_node_t *n = apol_vector_get_element(ttrules, l); + n->used = true; + if (apol_vector_append(tmpl_result->type_trans_rules, (void *)n->rule)) { + error = errno; + apol_vector_destroy(&ttrules); + apol_vector_destroy(&potential_start_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } + apol_vector_destroy(&ttrules); + apol_vector_sort_uniquify(tmpl_result->type_trans_rules, NULL, NULL); + dummy.type = tmpl_result->start_type; + dom_node_t *start_node = NULL; + apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&dummy, NULL, + (void **)&start_node); + if (start_node) { + //for each start check setexec if needed + if (requires_setexec_or_type_trans(policy)) { + apol_vector_destroy(&tmpl_result->setexec_rules); + tmpl_result->setexec_rules = apol_vector_create(NULL); + if (!tmpl_result->setexec_rules || + apol_vector_cat(tmpl_result->setexec_rules, + start_node->setexec_rules)) { + error = errno; + apol_vector_destroy(&potential_start_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } + //for each start find pt + apol_vector_destroy(&tmpl_result->proc_trans_rules); + tmpl_result->proc_trans_rules = apol_vector_create(NULL); + if (!tmpl_result->proc_trans_rules) { + error = errno; + apol_vector_destroy(&potential_start_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + apol_vector_t *pt_rules = NULL; + pt_rules = + find_avrules_in_node(start_node, APOL_DOMAIN_TRANS_RULE_PROC_TRANS, + tmpl_result->end_type); + if (apol_vector_get_size(pt_rules)) { + for (size_t l = 0; l < apol_vector_get_size(pt_rules); l++) { + avrule_node_t *n = apol_vector_get_element(pt_rules, l); + apol_vector_append(tmpl_result->proc_trans_rules, (void *)n->rule); + } + apol_vector_destroy(&pt_rules); + apol_vector_sort_uniquify(tmpl_result->proc_trans_rules, NULL, NULL); + // have all possible rules add this entry + apol_domain_trans_result_t *tmp = + apol_domain_trans_result_create_from_domain_trans_result + (tmpl_result); + if (!tmp || apol_vector_append(local_results, (void *)tmp)) { + error = errno; + apol_domain_trans_result_destroy(&tmp); + apol_vector_destroy(&potential_start_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + //reset process transition rules + apol_vector_destroy(&tmpl_result->proc_trans_rules); + tmpl_result->proc_trans_rules = apol_vector_create(NULL); + if (!tmpl_result->proc_trans_rules) { + error = errno; + apol_vector_destroy(&potential_start_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + //reset setexec rules + apol_vector_destroy(&tmpl_result->setexec_rules); + tmpl_result->setexec_rules = apol_vector_create(NULL); + if (!tmpl_result->setexec_rules) { + error = errno; + apol_vector_destroy(&potential_start_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } else { + //have entrypoint and execute rules but no process transition rule + apol_domain_trans_result_t *tmp = + apol_domain_trans_result_create_from_domain_trans_result + (tmpl_result); + if (!tmp || apol_vector_append(local_results, (void *)tmp)) { + error = errno; + apol_domain_trans_result_destroy(&tmp); + apol_vector_destroy(&potential_start_types); + apol_vector_destroy(&potential_ep_types); + apol_vector_destroy(&pt_rules); + goto err; + } + } + apol_vector_destroy(&pt_rules); + } else { + //have entrypoint and execute rules but no process transition rule + apol_domain_trans_result_t *tmp = + apol_domain_trans_result_create_from_domain_trans_result(tmpl_result); + if (!tmp || apol_vector_append(local_results, (void *)tmp)) { + error = errno; + apol_domain_trans_result_destroy(&tmp); + apol_vector_destroy(&potential_start_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } + //reset execute rules + apol_vector_destroy(&tmpl_result->exec_rules); + tmpl_result->exec_rules = apol_vector_create(NULL); + if (!tmpl_result->exec_rules) { + error = errno; + apol_vector_destroy(&potential_start_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + //reset type transition rules + apol_vector_destroy(&tmpl_result->type_trans_rules); + tmpl_result->type_trans_rules = apol_vector_create(NULL); + if (!tmpl_result->type_trans_rules) { + error = errno; + apol_vector_destroy(&potential_start_types); + apol_vector_destroy(&potential_ep_types); + goto err; + } + } + apol_vector_destroy(&potential_start_types); + } else { + //have entrypoint but no exec + apol_domain_trans_result_t *tmp = + apol_domain_trans_result_create_from_domain_trans_result(tmpl_result); + if (!tmp || apol_vector_append(local_results, (void *)tmp)) { + error = errno; + apol_domain_trans_result_destroy(&tmp); + goto err; + } + } + } + apol_vector_destroy(&potential_ep_types); + + //validate all + for (size_t i = 0; i < apol_vector_get_size(local_results); i++) { + apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i); + if (res->start_type && res->ep_type && res->end_type && apol_vector_get_size(res->proc_trans_rules) && + apol_vector_get_size(res->ep_rules) && apol_vector_get_size(res->exec_rules) && + (requires_setexec_or_type_trans(policy) + ? (apol_vector_get_size(res->setexec_rules) || apol_vector_get_size(res->type_trans_rules)) : true)) { + res->valid = true; + } + } + } + //iff looking for invalid find orphan type_transition rules + if (dta->valid & APOL_DOMAIN_TRANS_SEARCH_INVALID) { + if (domain_trans_table_find_orphan_type_transitions(policy, dta, local_results)) { + error = errno; + goto err; + } + } + + apol_domain_trans_result_destroy(&tmpl_result); + return 0; + + err: + apol_domain_trans_result_destroy(&tmpl_result); + errno = error; + return -1; +} + +int apol_domain_trans_analysis_do(apol_policy_t * policy, apol_domain_trans_analysis_t * dta, apol_vector_t ** results) +{ + apol_vector_t *local_results = NULL; + apol_avrule_query_t *accessq = NULL; + int error = 0; + if (!results) + *results = NULL; + if (!policy || !dta || !results) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + /* build table if not already present */ + if (!(policy->domain_trans_table)) { + if (apol_policy_build_domain_trans_table(policy)) + return -1; /* errors already reported by build function */ + } + + /* validate analysis options */ + if (dta->direction == 0 || dta->valid & ~(APOL_DOMAIN_TRANS_SEARCH_BOTH) || !(dta->start_type)) { + error = EINVAL; + ERR(policy, "%s", strerror(EINVAL)); + goto err; + } + size_t num_atypes = apol_vector_get_size(dta->access_types); + size_t num_aclasses = apol_vector_get_size(dta->access_classes); + size_t num_aprems = apol_vector_get_size(dta->access_perms); + if ((num_atypes == 0 && (num_aclasses != 0 || num_aprems != 0)) || + (num_aclasses == 0 && (num_atypes != 0 || num_aprems != 0)) || + (num_aprems == 0 && (num_aclasses != 0 || num_atypes != 0))) { + error = EINVAL; + ERR(policy, "%s", strerror(EINVAL)); + goto err; + } + + /* get starting type */ + const qpol_type_t *start_type = NULL; + if (qpol_policy_get_type_by_name(policy->p, dta->start_type, &start_type)) { + error = errno; + ERR(policy, "Unable to perform analysis: Invalid starting type %s", dta->start_type); + goto err; + } + unsigned char isattr = 0; + qpol_type_get_isattr(policy->p, start_type, &isattr); + if (isattr) { + ERR(policy, "%s", "Attributes are not valid here."); + error = EINVAL; + goto err; + } + + local_results = apol_vector_create(domain_trans_result_free); + /* get all transitions for the requested direction */ + if (dta->direction == APOL_DOMAIN_TRANS_DIRECTION_REVERSE) { + if (domain_trans_table_get_all_reverse_trans(policy, dta, local_results, start_type)) { + error = errno; + goto err; + } + } else { + if (domain_trans_table_get_all_forward_trans(policy, dta, local_results, start_type)) { + error = errno; + goto err; + } + } + + /* if requested, filter by validity */ + if (dta->valid != APOL_DOMAIN_TRANS_SEARCH_BOTH) { + for (size_t i = 0; i < apol_vector_get_size(local_results); /* increment later */ ) { + apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i); + if (res->valid != (dta->valid == APOL_DOMAIN_TRANS_SEARCH_VALID)) { + apol_vector_remove(local_results, i); + domain_trans_result_free(res); + } else { + i++; + } + } + } + + /* if filtering by result type, do that now */ + if (dta->result) { + for (size_t i = 0; i < apol_vector_get_size(local_results); /* increment later */ ) { + apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i); + const qpol_type_t *type = NULL; + if (dta->direction == APOL_DOMAIN_TRANS_DIRECTION_REVERSE) { + type = res->start_type; + } else { + type = res->end_type; + } + int compval = apol_compare_type(policy, type, dta->result, APOL_QUERY_REGEX, &dta->result_regex); + if (compval < 0) { + error = errno; + goto err; + } else if (compval > 0) { + i++; + } else { + apol_vector_remove(local_results, i); + domain_trans_result_free(res); + } + } + } + + /* finally do access filtering */ + if (dta->direction == APOL_DOMAIN_TRANS_DIRECTION_FORWARD && num_atypes && num_aclasses && num_aprems) { + accessq = apol_avrule_query_create(); + apol_avrule_query_set_rules(policy, accessq, QPOL_RULE_ALLOW); + for (size_t i = 0; i < num_aclasses; i++) { + if (apol_avrule_query_append_class + (policy, accessq, (char *)apol_vector_get_element(dta->access_classes, i))) { + error = errno; + goto err; + } + } + for (size_t i = 0; i < num_aprems; i++) { + if (apol_avrule_query_append_perm(policy, accessq, (char *)apol_vector_get_element(dta->access_perms, i))) { + error = errno; + goto err; + } + } + for (size_t i = 0; i < apol_vector_get_size(local_results); /* increment later */ ) { + const char *end_name = NULL; + apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i); + if (qpol_type_get_name(apol_policy_get_qpol(policy), res->end_type, &end_name) || + apol_avrule_query_set_source(policy, accessq, end_name, 1)) { + error = errno; + goto err; + } + apol_vector_t *tmp_access = apol_vector_create(NULL); + for (size_t j = 0; j < num_atypes; j++) { + if (apol_avrule_query_set_target + (policy, accessq, (char *)apol_vector_get_element(dta->access_types, j), 1)) { + error = errno; + apol_vector_destroy(&tmp_access); + goto err; + } + apol_vector_t *cur_tgt_v = NULL; + apol_avrule_get_by_query(policy, accessq, &cur_tgt_v); + apol_vector_cat(tmp_access, cur_tgt_v); + apol_vector_destroy(&cur_tgt_v); + } + if (apol_vector_get_size(tmp_access)) { + res->access_rules = tmp_access; + tmp_access = NULL; + i++; + } else { + apol_vector_remove(local_results, i); + domain_trans_result_free(res); + } + apol_vector_destroy(&tmp_access); + } + apol_avrule_query_destroy(&accessq); + } + + *results = apol_vector_create(domain_trans_result_free); + if (!(*results)) { + error = errno; + goto err; + } + for (size_t i = 0; i < apol_vector_get_size(local_results); i++) { + apol_domain_trans_result_t *res = + apol_domain_trans_result_create_from_domain_trans_result((apol_domain_trans_result_t *) + apol_vector_get_element(local_results, i)); + if (!res || apol_vector_append(*results, (void *)res)) { + error = errno; + domain_trans_result_free(res); + goto err; + } + } + apol_vector_destroy(&local_results); + + return 0; + err: + apol_vector_destroy(&local_results); + apol_vector_destroy(results); + apol_avrule_query_destroy(&accessq); + errno = error; + return -1; +} + +/* result */ + +const qpol_type_t *apol_domain_trans_result_get_start_type(const apol_domain_trans_result_t * dtr) +{ + if (dtr) { + return dtr->start_type; + } else { + errno = EINVAL; + return NULL; + } +} + +const qpol_type_t *apol_domain_trans_result_get_entrypoint_type(const apol_domain_trans_result_t * dtr) +{ + if (dtr) { + return dtr->ep_type; + } else { + errno = EINVAL; + return NULL; + } +} + +const qpol_type_t *apol_domain_trans_result_get_end_type(const apol_domain_trans_result_t * dtr) +{ + if (dtr) { + return dtr->end_type; + } else { + errno = EINVAL; + return NULL; + } +} + +const apol_vector_t *apol_domain_trans_result_get_proc_trans_rules(const apol_domain_trans_result_t * dtr) +{ + if (dtr) { + return dtr->proc_trans_rules; + } else { + errno = EINVAL; + return NULL; + } +} + +const apol_vector_t *apol_domain_trans_result_get_entrypoint_rules(const apol_domain_trans_result_t * dtr) +{ + if (dtr) { + return dtr->ep_rules; + } else { + errno = EINVAL; + return NULL; + } +} + +const apol_vector_t *apol_domain_trans_result_get_exec_rules(const apol_domain_trans_result_t * dtr) +{ + if (dtr) { + return dtr->exec_rules; + } else { + errno = EINVAL; + return NULL; + } +} + +const apol_vector_t *apol_domain_trans_result_get_setexec_rules(const apol_domain_trans_result_t * dtr) +{ + if (dtr) { + return dtr->setexec_rules; + } else { + errno = EINVAL; + return NULL; + } +} + +const apol_vector_t *apol_domain_trans_result_get_type_trans_rules(const apol_domain_trans_result_t * dtr) +{ + if (dtr) { + return dtr->type_trans_rules; + } else { + errno = EINVAL; + return NULL; + } +} + +int apol_domain_trans_result_is_trans_valid(const apol_domain_trans_result_t * dtr) +{ + if (dtr) { + return dtr->valid; + } else { + errno = EINVAL; + return 0; + } +} + +const apol_vector_t *apol_domain_trans_result_get_access_rules(const apol_domain_trans_result_t * dtr) +{ + if (dtr) { + return dtr->access_rules; + } else { + errno = EINVAL; + return NULL; + } +} + +int apol_domain_trans_table_verify_trans(apol_policy_t * policy, const qpol_type_t * start_dom, const qpol_type_t * ep_type, + const qpol_type_t * end_dom) +{ + int missing_rules = 0; + + if (!policy || !policy->domain_trans_table) { + errno = EINVAL; + return -1; + } + //reset the table + apol_policy_reset_domain_trans_table(policy); + //find nodes for each type + dom_node_t start_dummy = { start_dom, NULL, NULL, NULL }; + dom_node_t *start_node = NULL; + if (start_dom) + apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&start_dummy, NULL, (void **)&start_node); + ep_node_t ep_dummy = { ep_type, NULL, NULL }; + ep_node_t *ep_node = NULL; + if (ep_type) + apol_bst_get_element(policy->domain_trans_table->entrypoint_table, (void *)&ep_dummy, NULL, (void **)&ep_node); + dom_node_t end_dummy = { end_dom, NULL, NULL, NULL }; + dom_node_t *end_node = NULL; + if (end_dom) + apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&end_dummy, NULL, (void **)&end_node); + + bool tt = false, sx = false, ex = false, pt = false, ep = false; + + //find process transition rule + if (start_node && end_dom) { + apol_vector_t *v = find_avrules_in_node(start_node, APOL_DOMAIN_TRANS_RULE_PROC_TRANS, end_dom); + if (apol_vector_get_size(v)) + pt = true; + apol_vector_destroy(&v); + } + //find execute rule + if (start_dom && ep_node) { + apol_vector_t *v = find_avrules_in_node(ep_node, APOL_DOMAIN_TRANS_RULE_EXEC, start_dom); + if (apol_vector_get_size(v)) + ex = true; + apol_vector_destroy(&v); + } + //find entrypoint rules + if (end_node && ep_type) { + apol_vector_t *v = find_avrules_in_node(end_node, APOL_DOMAIN_TRANS_RULE_ENTRYPOINT, ep_type); + if (apol_vector_get_size(v)) + ep = true; + apol_vector_destroy(&v); + } + if (requires_setexec_or_type_trans(policy)) { + //find setexec rule + if (start_node) + if (apol_vector_get_size(start_node->setexec_rules)) + sx = true; + //find type_transition rule + if (ep_node && start_dom && end_dom) { + apol_vector_t *v = find_terules_in_node(ep_node, start_dom, end_dom); + if (apol_vector_get_size(v)) { + tt = true; + } + apol_vector_destroy(&v); + } + } else { + //old policy version - pretend these exist + tt = sx = true; + } + + if (!(pt && ep && ex && (tt || sx))) { + if (!pt) + missing_rules |= APOL_DOMAIN_TRANS_RULE_PROC_TRANS; + if (!ep) + missing_rules |= APOL_DOMAIN_TRANS_RULE_ENTRYPOINT; + if (!ex) + missing_rules |= APOL_DOMAIN_TRANS_RULE_EXEC; + if (!tt && !sx) { + missing_rules |= APOL_DOMAIN_TRANS_RULE_SETEXEC; + //do not report type_transition as missing if there is one for another entrypoint as this would be invalid + const char *start_name = NULL, *end_name = NULL; + qpol_type_get_name(apol_policy_get_qpol(policy), start_dom, &start_name); + qpol_type_get_name(apol_policy_get_qpol(policy), end_dom, &end_name); + apol_terule_query_t *tq = NULL; + if (!start_name || !end_name || !(tq = apol_terule_query_create())) { + return -1; + } + apol_terule_query_set_rules(policy, tq, QPOL_RULE_TYPE_TRANS); + apol_terule_query_set_source(policy, tq, start_name, 1); + apol_terule_query_set_default(policy, tq, end_name); + apol_vector_t *v = NULL; + if (apol_terule_get_by_query(policy, tq, &v)) { + apol_terule_query_destroy(&tq); + return -1; + } + apol_terule_query_destroy(&tq); + if (!apol_vector_get_size(v)) + missing_rules |= APOL_DOMAIN_TRANS_RULE_TYPE_TRANS; + apol_vector_destroy(&v); + } + } + + return missing_rules; +} + +apol_domain_trans_result_t *apol_domain_trans_result_create_from_domain_trans_result(const apol_domain_trans_result_t * result) +{ + apol_domain_trans_result_t *new_r = NULL; + int retval = -1; + if ((new_r = calloc(1, sizeof(*new_r))) == NULL) { + goto cleanup; + } + if (result->proc_trans_rules != NULL && + (new_r->proc_trans_rules = apol_vector_create_from_vector(result->proc_trans_rules, NULL, NULL, NULL)) == NULL) { + goto cleanup; + } + if (result->ep_rules != NULL + && (new_r->ep_rules = apol_vector_create_from_vector(result->ep_rules, NULL, NULL, NULL)) == NULL) { + goto cleanup; + } + if (result->exec_rules != NULL + && (new_r->exec_rules = apol_vector_create_from_vector(result->exec_rules, NULL, NULL, NULL)) == NULL) { + goto cleanup; + } + if (result->setexec_rules != NULL + && (new_r->setexec_rules = apol_vector_create_from_vector(result->setexec_rules, NULL, NULL, NULL)) == NULL) { + goto cleanup; + } + if (result->type_trans_rules != NULL && + (new_r->type_trans_rules = apol_vector_create_from_vector(result->type_trans_rules, NULL, NULL, NULL)) == NULL) { + goto cleanup; + } + if (result->access_rules != NULL + && (new_r->access_rules = apol_vector_create_from_vector(result->access_rules, NULL, NULL, NULL)) == NULL) { + goto cleanup; + } + new_r->start_type = result->start_type; + new_r->ep_type = result->ep_type; + new_r->end_type = result->end_type; + new_r->valid = result->valid; + retval = 0; + cleanup: + if (retval != 0) { + domain_trans_result_free(new_r); + return NULL; + } + return new_r; +} + +/******************** protected functions ********************/ + +void domain_trans_result_free(void *dtr) +{ + apol_domain_trans_result_t *res = (apol_domain_trans_result_t *) dtr; + + if (!res) + return; + + apol_vector_destroy(&res->proc_trans_rules); + apol_vector_destroy(&res->ep_rules); + apol_vector_destroy(&res->exec_rules); + apol_vector_destroy(&res->setexec_rules); + apol_vector_destroy(&res->type_trans_rules); + apol_vector_destroy(&res->access_rules); + free(res); +} + +void apol_domain_trans_result_destroy(apol_domain_trans_result_t ** res) +{ + if (!res || !(*res)) + return; + domain_trans_result_free((void *)*res); + *res = NULL; +} diff --git a/libapol/src/fscon-query.c b/libapol/src/fscon-query.c new file mode 100644 index 0000000..be3c7d3 --- /dev/null +++ b/libapol/src/fscon-query.c @@ -0,0 +1,437 @@ +/** + * @file + * + * Provides a way for setools to make queries about genfscons and + * fs_use statements within a policy. The caller obtains a query + * object, fills in its parameters, and then runs the query; it + * obtains a vector of results. Searches are conjunctive -- all + * fields of the search query must match for a datum to be added to + * the results query. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" +#include <apol/render.h> + +#include <errno.h> +#include <stdio.h> +#include <stdbool.h> + +struct apol_genfscon_query +{ + char *fs, *path; + uint32_t objclass; + bool objclass_set; + apol_context_t *context; + unsigned int flags; +}; + +struct apol_fs_use_query +{ + char *fs; + uint32_t behavior; + bool behavior_set; + apol_context_t *context; + unsigned int flags; +}; + +/******************** genfscon queries ********************/ + +int apol_genfscon_get_by_query(const apol_policy_t * p, const apol_genfscon_query_t * g, apol_vector_t ** v) +{ + qpol_iterator_t *iter; + int retval = -1, retval2; + qpol_genfscon_t *genfscon = NULL; + *v = NULL; + if (qpol_policy_get_genfscon_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(free)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&genfscon) < 0) { + goto cleanup; + } + if (g != NULL) { + const char *fs, *path; + uint32_t objclass; + const qpol_context_t *context; + if (qpol_genfscon_get_name(p->p, genfscon, &fs) < 0 || + qpol_genfscon_get_path(p->p, genfscon, &path) < 0 || + qpol_genfscon_get_class(p->p, genfscon, &objclass) < 0 || + qpol_genfscon_get_context(p->p, genfscon, &context) < 0) { + goto cleanup; + } + retval2 = apol_compare(p, fs, g->fs, 0, NULL); + if (retval2 < 0) { + goto cleanup; + } else if (retval2 == 0) { + free(genfscon); + continue; + } + retval2 = apol_compare(p, path, g->path, 0, NULL); + if (retval2 < 0) { + goto cleanup; + } else if (retval2 == 0) { + free(genfscon); + continue; + } + if (g->objclass_set && g->objclass != objclass) { + free(genfscon); + continue; + } + retval2 = apol_compare_context(p, context, g->context, g->flags); + if (retval2 < 0) { + goto cleanup; + } else if (retval2 == 0) { + free(genfscon); + continue; + } + } + if (apol_vector_append(*v, genfscon)) { + ERR(p, "%s", strerror(EINVAL)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + free(genfscon); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_genfscon_query_t *apol_genfscon_query_create(void) +{ + apol_genfscon_query_t *g = calloc(1, sizeof(apol_genfscon_query_t)); + if (g != NULL) { + g->objclass = -1; + } + return g; +} + +void apol_genfscon_query_destroy(apol_genfscon_query_t ** g) +{ + if (*g != NULL) { + free((*g)->fs); + free((*g)->path); + apol_context_destroy(&((*g)->context)); + free(*g); + *g = NULL; + } +} + +int apol_genfscon_query_set_filesystem(const apol_policy_t * p, apol_genfscon_query_t * g, const char *fs) +{ + return apol_query_set(p, &g->fs, NULL, fs); +} + +int apol_genfscon_query_set_path(const apol_policy_t * p, apol_genfscon_query_t * g, const char *path) +{ + int tmp = apol_query_set(p, &g->path, NULL, path); + if (!tmp && g->path) { + if (strlen(g->path) > 1 && g->path[strlen(g->path) - 1] == '/') + g->path[strlen(g->path) - 1] = 0; + } + return tmp; +} + +int apol_genfscon_query_set_objclass(const apol_policy_t * p, apol_genfscon_query_t * g, int objclass) +{ + if (objclass < 0) { + g->objclass = 0; + g->objclass_set = false; + } else { + switch (objclass) { + case QPOL_CLASS_BLK_FILE: + case QPOL_CLASS_CHR_FILE: + case QPOL_CLASS_DIR: + case QPOL_CLASS_FIFO_FILE: + case QPOL_CLASS_FILE: + case QPOL_CLASS_LNK_FILE: + case QPOL_CLASS_SOCK_FILE: + case QPOL_CLASS_ALL: + { + g->objclass = objclass; + g->objclass_set = true; + break; + } + default: + ERR(p, "%s", "Invalid object class given."); + return -1; + } + } + return 0; +} + +int apol_genfscon_query_set_context(const apol_policy_t * p __attribute__ ((unused)), + apol_genfscon_query_t * g, apol_context_t * context, unsigned int range_match) +{ + if (g->context != NULL) { + apol_context_destroy(&g->context); + } + g->context = context; + g->flags = (g->flags & ~APOL_QUERY_FLAGS) | range_match; + return 0; +} + +char *apol_genfscon_render(const apol_policy_t * p, const qpol_genfscon_t * genfscon) +{ + char *line = NULL, *retval = NULL; + const qpol_context_t *ctxt = NULL; + char *context_str = NULL; + const char *type_str = NULL; + const char *name = NULL, *path = NULL; + uint32_t fclass; + + if (!genfscon || !p) + goto cleanup; + + if (qpol_genfscon_get_name(p->p, genfscon, &name)) + goto cleanup; + if (qpol_genfscon_get_path(p->p, genfscon, &path)) + goto cleanup; + if (qpol_genfscon_get_class(p->p, genfscon, &fclass)) + return NULL; + if (qpol_genfscon_get_context(p->p, genfscon, &ctxt)) + goto cleanup; + + switch (fclass) { + case QPOL_CLASS_DIR: + type_str = " -d "; + break; + case QPOL_CLASS_CHR_FILE: + type_str = " -c "; + break; + case QPOL_CLASS_BLK_FILE: + type_str = " -b "; + break; + case QPOL_CLASS_FILE: + type_str = " -- "; + break; + case QPOL_CLASS_FIFO_FILE: + type_str = " -p "; + break; + case QPOL_CLASS_LNK_FILE: + type_str = " -l "; + break; + case QPOL_CLASS_SOCK_FILE: + type_str = " -s "; + break; + case QPOL_CLASS_ALL: + type_str = " "; + break; + default: + goto cleanup; + break; + } + context_str = apol_qpol_context_render(p, ctxt); + if (!context_str) + goto cleanup; + + if (asprintf(&line, "genfscon %s %s %s %s", name, path, type_str, context_str) < 0) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + retval = line; + cleanup: + free(context_str); + if (retval != line) { + free(line); + } + return retval; +} + +/******************** fs_use queries ********************/ + +int apol_fs_use_get_by_query(const apol_policy_t * p, const apol_fs_use_query_t * f, apol_vector_t ** v) +{ + qpol_iterator_t *iter; + int retval = -1, retval2; + const qpol_fs_use_t *fs_use = NULL; + *v = NULL; + if (qpol_policy_get_fs_use_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&fs_use) < 0) { + goto cleanup; + } + if (f != NULL) { + const char *fs; + uint32_t behavior; + const qpol_context_t *context = NULL; + if (qpol_fs_use_get_name(p->p, fs_use, &fs) < 0 || qpol_fs_use_get_behavior(p->p, fs_use, &behavior) < 0) { + goto cleanup; + } + if (behavior != QPOL_FS_USE_PSID && qpol_fs_use_get_context(p->p, fs_use, &context) < 0) { + goto cleanup; + } + retval2 = apol_compare(p, fs, f->fs, 0, NULL); + if (retval2 < 0) { + goto cleanup; + } else if (retval2 == 0) { + continue; + } + if (f->behavior_set && f->behavior != behavior) { + continue; + } + /* recall that fs_use_psid statements do not + * have contexts */ + if (f->context != NULL && behavior == QPOL_FS_USE_PSID) { + retval2 = 0; + } else { + retval2 = apol_compare_context(p, context, f->context, f->flags); + if (retval2 < 0) { + goto cleanup; + } + } + if (retval2 == 0) { + continue; + } + } + if (apol_vector_append(*v, (void *)fs_use)) { + ERR(p, "%s", strerror(EINVAL)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_fs_use_query_t *apol_fs_use_query_create(void) +{ + apol_fs_use_query_t *f = calloc(1, sizeof(apol_fs_use_query_t)); + if (f != NULL) { + f->behavior = -1; + } + return f; +} + +void apol_fs_use_query_destroy(apol_fs_use_query_t ** f) +{ + if (*f != NULL) { + free((*f)->fs); + apol_context_destroy(&((*f)->context)); + free(*f); + *f = NULL; + } +} + +int apol_fs_use_query_set_filesystem(const apol_policy_t * p, apol_fs_use_query_t * f, const char *fs) +{ + return apol_query_set(p, &f->fs, NULL, fs); +} + +int apol_fs_use_query_set_behavior(const apol_policy_t * p, apol_fs_use_query_t * f, int behavior) +{ + if (behavior < 0) { + f->behavior = 0; + f->behavior_set = false; + } else { + switch (behavior) { + case QPOL_FS_USE_XATTR: + case QPOL_FS_USE_TASK: + case QPOL_FS_USE_TRANS: + case QPOL_FS_USE_GENFS: + case QPOL_FS_USE_NONE: + case QPOL_FS_USE_PSID: + { + f->behavior = behavior; + f->behavior_set = true; + break; + } + default: + ERR(p, "%s", "Invalid fs_use behavior given."); + return -1; + } + } + return 0; +} + +int apol_fs_use_query_set_context(const apol_policy_t * p __attribute__ ((unused)), + apol_fs_use_query_t * f, apol_context_t * context, unsigned int range_match) +{ + if (f->context != NULL) { + apol_context_destroy(&f->context); + } + f->context = context; + f->flags = (f->flags & ~APOL_QUERY_FLAGS) | range_match; + return 0; +} + +char *apol_fs_use_render(const apol_policy_t * p, const qpol_fs_use_t * fsuse) +{ + char *context_str = NULL; + char *line = NULL, *retval = NULL; + const char *behavior_str = NULL; + const char *fsname = NULL; + const qpol_context_t *ctxt = NULL; + uint32_t behavior; + + if (qpol_fs_use_get_behavior(p->p, fsuse, &behavior)) + goto cleanup; + if ((behavior_str = apol_fs_use_behavior_to_str(behavior)) == NULL) { + ERR(p, "%s", "Could not get behavior string."); + goto cleanup; + } + + if (qpol_fs_use_get_name(p->p, fsuse, &fsname)) + goto cleanup; + + if (behavior == QPOL_FS_USE_PSID) { + context_str = strdup(""); + } else { + if (qpol_fs_use_get_context(p->p, fsuse, &ctxt)) + goto cleanup; + context_str = apol_qpol_context_render(p, ctxt); + if (!context_str) { + goto cleanup; + } + } + if (asprintf(&line, "%s %s %s", behavior_str, fsname, context_str) < 0) { + ERR(p, "%s", strerror(EINVAL)); + goto cleanup; + } + + retval = line; + cleanup: + free(context_str); + if (retval != line) { + free(line); + } + return retval; +} diff --git a/libapol/src/infoflow-analysis-internal.h b/libapol/src/infoflow-analysis-internal.h new file mode 100644 index 0000000..61fdd85 --- /dev/null +++ b/libapol/src/infoflow-analysis-internal.h @@ -0,0 +1,49 @@ +/** + * @file + * + * Protected routines for information flow analysis. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_INFOFLOW_ANALYSIS_INTERNAL_H +#define APOL_INFOFLOW_ANALYSIS_INTERNAL_H + +/** + * Do a deep copy (i.e., a clone) of an apol_infoflow_result_t object. + * The caller is responsible for calling apol_infoflow_result_free() + * upon the returned value. + * + * @param result Pointer to an infoflow result structure to destroy. + * + * @return A clone of the passed in result node, or NULL upon error. + */ +extern apol_infoflow_result_t *infoflow_result_create_from_infoflow_result(const apol_infoflow_result_t * result); + +/** + * Free all memory associated with an information flow analysis + * result, including the pointer itself. This function does nothing + * if the result is already NULL. + * + * @param result Pointer to an infoflow result structure to destroy. + */ +extern void infoflow_result_free(void *result); + +#endif diff --git a/libapol/src/infoflow-analysis.c b/libapol/src/infoflow-analysis.c new file mode 100644 index 0000000..dad9a34 --- /dev/null +++ b/libapol/src/infoflow-analysis.c @@ -0,0 +1,2245 @@ +/** + * @file + * Implementation of the information flow analysis. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" +#include "infoflow-analysis-internal.h" +#include "queue.h" +#include <apol/bst.h> +#include <apol/perm-map.h> + +#include <assert.h> +#include <config.h> +#include <errno.h> +#include <limits.h> +#include <time.h> + +/* + * Nodes in the graph represent either a type used in the source + * of an allow rule or the target: these defines are used to + * represent which. + */ +#define APOL_INFOFLOW_NODE_SOURCE 0x1 +#define APOL_INFOFLOW_NODE_TARGET 0x2 + +/* + * These defines are used to color nodes in the graph algorithms. + */ +#define APOL_INFOFLOW_COLOR_WHITE 0 +#define APOL_INFOFLOW_COLOR_GREY 1 +#define APOL_INFOFLOW_COLOR_BLACK 2 +#define APOL_INFOFLOW_COLOR_RED 3 + +typedef struct apol_infoflow_node apol_infoflow_node_t; +typedef struct apol_infoflow_edge apol_infoflow_edge_t; + +struct apol_infoflow_graph +{ + /** vector of apol_infoflow_node_t */ + apol_vector_t *nodes; + /** vector of apol_infoflow_edge_t */ + apol_vector_t *edges; + /** temporary BST of apol_infoflow_node_t used while building + * the graph */ + apol_bst_t *nodes_bst; + + unsigned int mode, direction; + regex_t *regex; + + /** vector of apol_infoflow_node_t, used for random restarts + * for further transitive analysis */ + apol_vector_t *further_start; + /** vector of apol_infoflow_node_t of targets, used for + * further transitive analysis */ + apol_vector_t *further_end; + size_t current_start; +#ifdef HAVE_RAND_R + unsigned int seed; +#endif +}; + +struct apol_infoflow_node +{ + const qpol_type_t *type; + /** one of APOL_INFOFLOW_NODE_SOURCE or APOL_INFOFLOW_NODE_TARGET */ + int node_type; + /** vector of apol_infoflow_edge_t, pointing into the graph */ + apol_vector_t *in_edges; + /** vector of apol_infoflow_edge_t, pointing into the graph */ + apol_vector_t *out_edges; + unsigned char color; + apol_infoflow_node_t *parent; + int distance; +}; + +struct apol_infoflow_edge +{ + /** vector of qpol_avrule_t, pointing into the policy */ + apol_vector_t *rules; + /** pointer into a node within the graph */ + apol_infoflow_node_t *start_node; + /** pointer into a node within the graph */ + apol_infoflow_node_t *end_node; + int length; +}; + +/** + * apol_infoflow_analysis_h encapsulates all of the paramaters of a + * query. It should always be allocated with + * apol_infoflow_analysis_create() and deallocated with + * apol_infoflow_analysis_destroy(). Limiting by ending_types, + * obj_classes, intermed types, obj_class permissions is optional - if + * the vector is empty then no limiting is done. + * + * All of the vectors except end_types should contain the items that + * you want to not appear in the results. end_types lists the types + * that you do want to appear. + */ +struct apol_infoflow_analysis +{ + unsigned int mode, direction; + char *type, *result; + apol_vector_t *intermed, *class_perms; + int min_weight; +}; + +/** + * The results of running an infoflow, either direct or transitive, is + * a path from start_type to end_type. The path consists of a vector + * of intermediate steps. + */ +struct apol_infoflow_result +{ + const qpol_type_t *start_type, *end_type; + /** vector of apol_infoflow_step_t */ + apol_vector_t *steps; + unsigned int direction; + unsigned int length; +}; + +/** + * Each result consists of multiple steps, representing the steps + * taken from the original start to end types. Along each step there + * is a vector of rules. For a direct infoflow analysis there will be + * exactly one step, and that flow's start type is the same as the + * original result's start_type. Likewise the end_types will be the + * same. + */ +struct apol_infoflow_step +{ + const qpol_type_t *start_type, *end_type; + /** vector of qpol_avrule_t */ + apol_vector_t *rules; + int weight; +}; + +/** + * Deallocate all space associated with an apol_infoflow_step_t, + * including the pointer itself. Does nothing if the pointer is + * already NULL. + * + * @param step Infoflow step to free. + */ +static void apol_infoflow_step_free(void *step) +{ + if (step != NULL) { + apol_infoflow_step_t *s = (apol_infoflow_step_t *) step; + apol_vector_destroy(&s->rules); + free(s); + } +} + +/******************** random number routines ********************/ + +/** + * Initialize the pseudo-random number generator to be used during + * further transitive analysis. + * + * @param g Transitive infoflow graph. + */ +static void apol_infoflow_srand(apol_infoflow_graph_t * g) +{ +#ifdef HAVE_RAND_R + g->seed = (int)time(NULL); +#else + srand((int)time(NULL)); +#endif +} + +/** + * Return a pseudo-random integer between 0 and RAND_MAX, for use + * during further transitive analysis. If the system supports it, + * this function will use rand_r() so that this library remains + * reentrant and thread-safe. + * + * @param g Transitive infoflow graph. + * + * @return Integer between 0 and RAND_MAX. + */ +static int apol_infoflow_rand(apol_infoflow_graph_t * g) +{ +#ifdef HAVE_RAND_R + return rand_r(&g->seed); +#else + return rand(); +#endif +} + +/******************** infoflow graph node routines ********************/ + +/** + * Given a pointer to an apol_infoflow_node_t, free its space + * including the pointer itself. Does nothing if the pointer is + * already NULL. + * + * @param data Node to free. + */ +static void apol_infoflow_node_free(void *data) +{ + apol_infoflow_node_t *node = (apol_infoflow_node_t *) data; + if (node != NULL) { + /* the edges themselves are owned by the graph, not by + * the node */ + apol_vector_destroy(&node->in_edges); + apol_vector_destroy(&node->out_edges); + free(node); + } +} + +struct apol_infoflow_node_key +{ + const qpol_type_t *type; + int node_type; +}; + +/** + * Given an infoflow node and a key, returns 0 if they are the same, + * non-zero if not. + * + * @param a Existing node within the infoflow graph. + * @param b <i>Unused.</i> + * @param data Pointer to a struct infoflow_node_key. + * + * @return 0 if the key matches a, non-zero if not. + */ +static int apol_infoflow_node_compare(const void *a, const void *b __attribute__ ((unused)), void *data) +{ + apol_infoflow_node_t *node = (apol_infoflow_node_t *) a; + struct apol_infoflow_node_key *key = (struct apol_infoflow_node_key *)data; + if (node->type != key->type) { + return (int)((char *)node->type - (char *)key->type); + } + return node->node_type - key->node_type; +} + +/** + * Attempt to allocate a new node, add it to the infoflow graph, and + * return a pointer to it. If there already exists a node with the + * same type then reuse that node. + * + * @param p Policy handler, for reporting error. + * @param g Infoflow to which add the node. + * @param type Type for the new node. + * @param node_type Node type, one of APOL_INFOFLOW_NODE_SOURCE or + * APOL_INFOFLOW_NODE_TARGET. + * + * @return Pointer an allocated node within the infoflow graph, or + * NULL upon error. + */ +static apol_infoflow_node_t *apol_infoflow_graph_create_node(const apol_policy_t * p, + apol_infoflow_graph_t * g, const qpol_type_t * type, int node_type) +{ + struct apol_infoflow_node_key key = { type, node_type }; + apol_infoflow_node_t *node = NULL; + if (apol_bst_get_element(g->nodes_bst, NULL, &key, (void **)&node) == 0) { + return node; + } + if ((node = calloc(1, sizeof(*node))) == NULL || + (node->in_edges = apol_vector_create(NULL)) == NULL || (node->out_edges = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + apol_infoflow_node_free(node); + return NULL; + } + node->type = type; + node->node_type = node_type; + if (apol_bst_insert(g->nodes_bst, node, &key) != 0) { + ERR(p, "%s", strerror(errno)); + apol_infoflow_node_free(node); + return NULL; + } + return node; +} + +/** + * Attempt to allocate a new node, add it to the infoflow graph, and + * return a pointer to it. If there already exists a node with the + * same type then reuse that node. + * + * @param p Policy handler, for reporting error. + * @param g Infoflow to which add the node. + * @param type Type for the new node. If this is an attribute then it + * will be expanded into its component types. + * @param types If non-NULL, a BST of qpol_type_t pointers. Only + * create and return nodes which are members of this tree. + * @param node_type Node type, one of APOL_INFOFLOW_NODE_SOURCE or + * APOL_INFOFLOW_NODE_TARGET. + * + * @return Vector of nodes (type apol_infoflow_node_t *) within the + * infoflow graph, or NULL upon error. The caller is responsible for + * calling apol_vector_destroy() upon the return value. + */ +static apol_vector_t *apol_infoflow_graph_create_nodes(const apol_policy_t * p, + apol_infoflow_graph_t * g, const qpol_type_t * type, apol_bst_t * types, + int node_type) +{ + unsigned char isattr; + apol_vector_t *v = NULL; + apol_infoflow_node_t *node = NULL; + if (qpol_type_get_isattr(p->p, type, &isattr) < 0) { + return NULL; + } + if (isattr && g->mode != APOL_INFOFLOW_MODE_DIRECT) { + qpol_iterator_t *iter = NULL; + qpol_type_t *t; + size_t len; + if (qpol_type_get_type_iter(p->p, type, &iter) < 0 || + qpol_iterator_get_size(iter, &len) < 0 || (v = apol_vector_create_with_capacity(len, NULL)) == NULL) { + qpol_iterator_destroy(&iter); + apol_vector_destroy(&v); + return NULL; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_iterator_get_item(iter, (void **)&t); + void *result; + if (types != NULL && apol_bst_get_element(types, t, NULL, &result) < 0) { + continue; + } + if ((node = apol_infoflow_graph_create_node(p, g, t, node_type)) == NULL || apol_vector_append(v, node) < 0) { + qpol_iterator_destroy(&iter); + apol_vector_destroy(&v); + return NULL; + } + } + qpol_iterator_destroy(&iter); + } else { + /* for a direct search, do not expand types; the + * algorithm will do that with + * apol_infoflow_graph_get_nodes_for_type() and + * apol_infoflow_analysis_direct_expand(). for + * transitive searches the \a types BST was checked in + * apol_infoflow_graph_check_types() if \a type is + * just a type. + */ + if ((v = apol_vector_create_with_capacity(1, NULL)) == NULL) { + return NULL; + } + if ((node = apol_infoflow_graph_create_node(p, g, type, node_type)) == NULL || apol_vector_append(v, node) < 0) { + apol_vector_destroy(&v); + return NULL; + } + } + return v; +} + +/******************** infoflow graph edge routines ********************/ + +/** + * Given a pointer to an apol_infoflow_edge_t, free its space + * including the pointer itself. Does nothing if the pointer is + * already NULL. + * + * @param data Edge to free. + */ +static void apol_infoflow_edge_free(void *data) +{ + apol_infoflow_edge_t *edge = (apol_infoflow_edge_t *) data; + if (edge != NULL) { + apol_vector_destroy(&edge->rules); + free(edge); + } +} + +struct apol_infoflow_edge_key +{ + apol_infoflow_node_t *start_node, *end_node; +}; + +/** + * Given an infoflow edge and a key, returns 0 if they are the same, + * non-zero if not. + * + * @param a Existing edge within the infoflow graph. + * @param b <i>Unused.</i> + * @param data Pointer to a struct infoflow_edge_key. + * + * @return 0 if the key matches a, non-zero if not. + */ +static int apol_infoflow_edge_compare(const void *a, const void *b __attribute__ ((unused)), void *data) +{ + apol_infoflow_edge_t *edge = (apol_infoflow_edge_t *) a; + struct apol_infoflow_edge_key *key = (struct apol_infoflow_edge_key *)data; + if (key->start_node != NULL && edge->start_node != key->start_node) { + return (int)((char *)edge->start_node - (char *)key->start_node); + } + if (key->end_node != NULL && edge->end_node != key->end_node) { + return (int)((char *)edge->end_node - (char *)key->end_node); + } + return 0; +} + +/** + * Attempt to allocate a new edge, add it to the infoflow graph, and + * return a pointer to it. If there already exists a edge from the + * start node to the end node then reuse that edge. + * + * @param p Policy handler, for reporting errors. + * @param g Infoflow graph to which add the edge. + * @param start_node Starting node for the edge. + * @param end_node Ending node for the edge. + * @param len Length of edge (proportionally inverse of permission weight) + * + * @return Pointer an allocated node within the infoflow graph, or + * NULL upon error. + */ +static apol_infoflow_edge_t *apol_infoflow_graph_create_edge(const apol_policy_t * p, + apol_infoflow_graph_t * g __attribute__ ((unused)), + apol_infoflow_node_t * start_node, + apol_infoflow_node_t * end_node, int len) +{ + struct apol_infoflow_edge_key key = { NULL, end_node }; + size_t i; + apol_infoflow_edge_t *edge = NULL; + if (apol_vector_get_index(start_node->out_edges, NULL, apol_infoflow_edge_compare, &key, &i) == 0) { + edge = (apol_infoflow_edge_t *) apol_vector_get_element(start_node->out_edges, i); + if (edge->length < len) { + edge->length = len; + } + return edge; + } + if ((edge = calloc(1, sizeof(*edge))) == NULL || (edge->rules = apol_vector_create(NULL)) == NULL || + apol_vector_append(g->edges, edge) < 0) { + ERR(p, "%s", strerror(errno)); + apol_infoflow_edge_free(edge); + return NULL; + } + edge->start_node = start_node; + edge->end_node = end_node; + edge->length = len; + if (apol_vector_append(start_node->out_edges, edge) < 0 || apol_vector_append(end_node->in_edges, edge) < 0) { + /* don't free the edge -- it is owned by the graph */ + ERR(p, "%s", strerror(errno)); + return NULL; + } + return edge; +} + +/******************** infoflow graph creation routines ********************/ + +/** + * Take an avrule within a policy and possibly add it to the infoflow + * graph. The rule's source and target type sets are expanded. If + * the rule is to be added, then add its end nodes as necessary, and + * an edge connecting those nodes as necessary, and then add the rule + * to the edge. + * + * @param p Policy containing rules. + * @param g Information flow graph being created. + * @param rule AV rule to use. + * @param types BST of qpol_type_t pointers; while adding avrules to + * the graph, only add those whose source and/or target is a member of + * \a types, if \a types is non-NULL. + * @param found_read Non-zero to indicate that this rule performs a + * read operation. + * @param read_len Length of the edge to create (proportionally + * inverse of permission weight). + * @param found_write Non-zero to indicate that this rule performs a + * write operation. + * @param write_len Length of the edge to create (proportionally + * inverse of permission weight). + * + * @return 0 on success, < 0 on error. + */ +static int apol_infoflow_graph_connect_nodes(const apol_policy_t * p, + apol_infoflow_graph_t * g, + const qpol_avrule_t * rule, + apol_bst_t * types, int found_read, int read_len, int found_write, int write_len) +{ + const qpol_type_t *src_type, *tgt_type; + apol_vector_t *src_nodes = NULL, *tgt_nodes = NULL; + size_t i, j; + apol_infoflow_node_t *src_node, *tgt_node; + apol_infoflow_edge_t *edge; + int retval = -1; + + if (qpol_avrule_get_source_type(p->p, rule, &src_type) < 0 || qpol_avrule_get_target_type(p->p, rule, &tgt_type) < 0) { + goto cleanup; + } + + if ((src_nodes = apol_infoflow_graph_create_nodes(p, g, src_type, types, APOL_INFOFLOW_NODE_SOURCE)) == NULL) { + goto cleanup; + } + if ((tgt_nodes = apol_infoflow_graph_create_nodes(p, g, tgt_type, types, APOL_INFOFLOW_NODE_TARGET)) == NULL) { + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(src_nodes); i++) { + src_node = apol_vector_get_element(src_nodes, i); + for (j = 0; j < apol_vector_get_size(tgt_nodes); j++) { + tgt_node = apol_vector_get_element(tgt_nodes, j); + if (found_read) { + if ((edge = apol_infoflow_graph_create_edge(p, g, tgt_node, src_node, read_len)) == NULL) { + goto cleanup; + } + if (apol_vector_append(edge->rules, (void *)rule) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + if (found_write) { + if ((edge = apol_infoflow_graph_create_edge(p, g, src_node, tgt_node, write_len)) == NULL) { + goto cleanup; + } + if (apol_vector_append(edge->rules, (void *)rule) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + } + } + retval = 0; + cleanup: + apol_vector_destroy(&src_nodes); + apol_vector_destroy(&tgt_nodes); + return retval; +} + +/** + * Given a policy and a partially completed infoflow graph, create the + * nodes and edges associated with a particular rule. + * + * @param p Policy from which to create the infoflow graph. + * @param g Infoflow graph being created. + * @param rule AV rule to add. + * @param types BST of qpol_type_t pointers; while adding avrules to + * the graph, only add those whose source and/or target is a member of + * \a types, if \a types is non-NULL. + * @param max_len Maximum permission length (i.e., inverse of + * permission weight) to consider when deciding to add this rule or + * not. + * + * @return 0 on success, < 0 on error. + */ +static int apol_infoflow_graph_create_avrule(const apol_policy_t * p, apol_infoflow_graph_t * g, const qpol_avrule_t * rule, + apol_bst_t * types, int max_len) +{ + const qpol_class_t *obj_class; + qpol_iterator_t *perm_iter = NULL; + const char *obj_class_name; + char *perm_name; + int found_read = 0, found_write = 0, perm_error = 0; + int read_len = INT_MAX, write_len = INT_MAX; + int retval = -1; + if (qpol_avrule_get_object_class(p->p, rule, &obj_class) < 0 || + qpol_class_get_name(p->p, obj_class, &obj_class_name) < 0 || qpol_avrule_get_perm_iter(p->p, rule, &perm_iter) < 0) { + goto cleanup; + } + + /* find read or write flows for each object class/perm pair */ + for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) { + int perm_map, perm_weight, len; + + if (qpol_iterator_get_item(perm_iter, (void **)&perm_name) < 0) { + goto cleanup; + } + if (apol_policy_get_permmap(p, obj_class_name, perm_name, &perm_map, &perm_weight) < 0) { + goto cleanup; + } + free(perm_name); + if (perm_map == APOL_PERMMAP_UNMAPPED) { + perm_error = 1; + continue; + } + len = APOL_PERMMAP_MAX_WEIGHT - perm_weight + 1; + if (len < APOL_PERMMAP_MIN_WEIGHT) { + len = APOL_PERMMAP_MIN_WEIGHT; + } else if (len > APOL_PERMMAP_MAX_WEIGHT) { + len = APOL_PERMMAP_MAX_WEIGHT; + } + if (perm_map & APOL_PERMMAP_READ) { + if (len < read_len && len <= max_len) { + found_read = 1; + read_len = len; + } + } + if (perm_map & APOL_PERMMAP_WRITE) { + if (len < write_len && len <= max_len) { + found_write = 1; + write_len = len; + } + } + } + + /* if we have found any flows then connect them within the graph */ + if ((found_read || found_write) && + apol_infoflow_graph_connect_nodes(p, g, rule, types, found_read, read_len, found_write, write_len) < 0) { + goto cleanup; + } + if (perm_error) { + WARN(p, "%s", "Not all of the permissions found had associated permission maps."); + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&perm_iter); + return retval; +} + +/** + * Given a vector of strings representing types, return a BST of + * qpol_type_t pointers consisting of those types, those types' + * attributes, and those types' aliases. + * + * @param p Policy within which to look up types, + * @param v Vector of type strings. + * + * @return BST of qpol_type_t pointers, or NULL on error. The caller + * is responsible for calling apol_bst_destroy() upon the returned + * value. + */ +static apol_bst_t *apol_infoflow_graph_create_required_types(const apol_policy_t * p, const apol_vector_t * v) +{ + apol_bst_t *types = NULL; + apol_vector_t *expanded_types = NULL; + size_t i; + char *s; + int retval = -1; + if ((types = apol_bst_create(NULL, NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(v); i++) { + s = (char *)apol_vector_get_element(v, i); + expanded_types = apol_query_create_candidate_type_list(p, s, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH); + if (expanded_types == NULL) { + goto cleanup; + } + for (size_t j = 0; j < apol_vector_get_size(expanded_types); j++) { + qpol_type_t *t = (qpol_type_t *) apol_vector_get_element(expanded_types, j); + if (apol_bst_insert(types, t, NULL) < 0) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + } + apol_vector_destroy(&expanded_types); + } + retval = 0; + cleanup: + apol_vector_destroy(&expanded_types); + if (retval != 0) { + apol_bst_destroy(&types); + } + return types; +} + +/** + * Determine if an av rule matches a list of qpol_type_t pointers. + * Both the source and target of the rule must be in the list. + * + * @param p Policy to which look up classes and permissions. + * @param rule AV rule to check. + * @param types BST of qpol_type_t, of which both the source and + * target types must be members. If NULL allow all types. + * + * @return 1 if rule matches, 0 if not, < 0 on error. + */ +static int apol_infoflow_graph_check_types(const apol_policy_t * p, const qpol_avrule_t * rule, const apol_bst_t * types) +{ + const qpol_type_t *source, *target; + void *result; + int retval = -1; + if (types == NULL) { + retval = 1; + goto cleanup; + } + if (qpol_avrule_get_source_type(p->p, rule, &source) < 0 || qpol_avrule_get_target_type(p->p, rule, &target) < 0) { + goto cleanup; + } + if (apol_bst_get_element(types, source, NULL, &result) < 0 || apol_bst_get_element(types, target, NULL, &result) < 0) { + retval = 0; + goto cleanup; + } + retval = 1; + cleanup: + return retval; +} + +/** + * Determine if an av rule matches a list of apol_obj_perm_t. The + * rule's class must match at least one item in the list, and at least + * one of the rule's permissions must be on the list. + * + * @param p Policy to which look up classes and permissions. + * @param rule AV rule to check. + * @param class_perms Vector of apol_obj_perm_t, of which rule's class + * and permissions must be a member. If NULL or empty then allow all + * classes and permissions. + * + * @return 1 if rule matches, 0 if not, < 0 on error. + */ +static int apol_infoflow_graph_check_class_perms(const apol_policy_t * p, const qpol_avrule_t * rule, + const apol_vector_t * class_perms) +{ + const qpol_class_t *obj_class; + const char *obj_name; + char *perm; + qpol_iterator_t *iter = NULL; + apol_obj_perm_t *obj_perm = NULL; + apol_vector_t *obj_perm_v = NULL; + size_t i; + int retval = -1; + + if (class_perms == NULL || apol_vector_get_size(class_perms) == 0) { + retval = 1; + goto cleanup; + } + if (qpol_avrule_get_object_class(p->p, rule, &obj_class) < 0 || qpol_class_get_name(p->p, obj_class, &obj_name) < 0) { + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(class_perms); i++) { + obj_perm = (apol_obj_perm_t *) apol_vector_get_element(class_perms, i); + if (strcmp(apol_obj_perm_get_obj_name(obj_perm), obj_name) == 0) { + obj_perm_v = apol_obj_perm_get_perm_vector(obj_perm); + break; + } + } + if (i >= apol_vector_get_size(class_perms)) { + retval = 0; /* no matching class */ + goto cleanup; + } + if (qpol_avrule_get_perm_iter(p->p, rule, &iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&perm) < 0) { + goto cleanup; + } + if (apol_vector_get_index(obj_perm_v, perm, apol_str_strcmp, NULL, &i) == 0) { + free(perm); + retval = 1; + goto cleanup; + } + free(perm); + } + retval = 0; /* no matching perm */ + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} + +/** + * Given a particular information flow analysis object, generate an + * infoflow graph relative to a particular policy. This graph is + * customized for the particular analysis. + * + * @param p Policy from which to create the infoflow graph. + * @param ia Parameters to tune the created graph. + * @param g Reference to where to store the graph. The caller is + * responsible for calling apol_infoflow_graph_destroy() upon this. + * + * @return 0 if the graph was created, < 0 on error. Upon error *g + * will be set to NULL. + */ +static int apol_infoflow_graph_create(const apol_policy_t * p, const apol_infoflow_analysis_t * ia, apol_infoflow_graph_t ** g) +{ + apol_bst_t *types = NULL; + qpol_iterator_t *iter = NULL; + int max_len = APOL_PERMMAP_MAX_WEIGHT - ia->min_weight + 1; + int compval, retval = -1; + + *g = NULL; + if (p->pmap == NULL) { + ERR(p, "%s", "A permission map must be loaded prior to building the infoflow graph."); + goto cleanup; + } + + INFO(p, "%s", "Generating information flow graph."); + if (ia->mode == APOL_INFOFLOW_MODE_TRANS && ia->intermed != NULL && + (types = apol_infoflow_graph_create_required_types(p, ia->intermed)) == NULL) { + goto cleanup; + } + + if ((*g = calloc(1, sizeof(**g))) == NULL || + ((*g)->nodes_bst = apol_bst_create(apol_infoflow_node_compare, apol_infoflow_node_free)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + (*g)->mode = ia->mode; + (*g)->direction = ia->direction; + if (ia->result != NULL && ia->result[0] != '\0') { + if (((*g)->regex = malloc(sizeof(regex_t))) == NULL || regcomp((*g)->regex, ia->result, REG_EXTENDED | REG_NOSUB)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + } + if (((*g)->edges = apol_vector_create(apol_infoflow_edge_free)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + + if (qpol_policy_get_avrule_iter(p->p, QPOL_RULE_ALLOW, &iter) < 0) { + goto cleanup; + } + + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_avrule_t *rule; + if (qpol_iterator_get_item(iter, (void **)&rule) < 0) { + goto cleanup; + } + compval = apol_infoflow_graph_check_types(p, rule, types); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + compval = apol_infoflow_graph_check_class_perms(p, rule, ia->class_perms); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + if (apol_infoflow_graph_create_avrule(p, *g, rule, types, max_len) < 0) { + goto cleanup; + } + } + + if (((*g)->nodes = apol_bst_get_vector((*g)->nodes_bst, 1)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + apol_bst_destroy(&(*g)->nodes_bst); + retval = 0; + cleanup: + apol_bst_destroy(&types); + qpol_iterator_destroy(&iter); + if (retval < 0) { + apol_infoflow_graph_destroy(g); + } + return retval; +} + +void apol_infoflow_graph_destroy(apol_infoflow_graph_t ** g) +{ + if (g != NULL && *g != NULL) { + apol_bst_destroy(&(*g)->nodes_bst); + apol_vector_destroy(&(*g)->nodes); + apol_vector_destroy(&(*g)->edges); + apol_vector_destroy(&(*g)->further_start); + apol_vector_destroy(&(*g)->further_end); + apol_regex_destroy(&(*g)->regex); + free(*g); + *g = NULL; + } +} + +/*************** infoflow graph direct analysis routines ***************/ + +/** + * Given a graph and a target type, append to vector v all nodes + * (apol_infoflow_node_t) within the graph that use that type, one of + * that type's aliases, or one of that type's attributes. This will + * also implicitly permutate across all of the type's object classes. + * + * @param p Error reporting handler. + * @param g Information flow graph containing nodes. + * @param type Target type name to find. + * @param v Initialized vector to which append nodes. + * + * @return 0 on success, < 0 on error. + */ +static int apol_infoflow_graph_get_nodes_for_type(const apol_policy_t * p, const apol_infoflow_graph_t * g, const char *type, + apol_vector_t * v) +{ + size_t i, j; + apol_vector_t *cand_list = NULL; + int retval = -1; + if ((cand_list = apol_query_create_candidate_type_list(p, type, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) { + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(g->nodes); i++) { + apol_infoflow_node_t *node; + node = (apol_infoflow_node_t *) apol_vector_get_element(g->nodes, i); + if (apol_vector_get_index(cand_list, node->type, NULL, NULL, &j) == 0 && apol_vector_append(v, node) < 0) { + goto cleanup; + } + } + retval = 0; + cleanup: + apol_vector_destroy(&cand_list); + return retval; +} + +/** + * Return a usable infoflow result object. If there already exists a + * result object within vector v with the same start and ending type + * then reuse that object. Otherwise allocate and return a new + * infoflow result with its start and end type fields set. + * + * @param p Policy handler, for reporting errors. + * @param v Non-null vector of infoflow results. + * @param start_type Starting type for returned infoflow result object. + * @param end_type Starting type for returned infoflow result object. + * + * @return A usable infoflow result object, or NULL upon error. + */ +static apol_infoflow_result_t *apol_infoflow_direct_get_result(const apol_policy_t * p, + apol_vector_t * v, const qpol_type_t * start_type, + const qpol_type_t * end_type) +{ + size_t i; + apol_infoflow_result_t *r; + for (i = 0; i < apol_vector_get_size(v); i++) { + r = (apol_infoflow_result_t *) apol_vector_get_element(v, i); + if (r->start_type == start_type && r->end_type == end_type) { + return r; + } + } + if ((r = calloc(1, sizeof(*r))) == NULL || (r->steps = apol_vector_create(apol_infoflow_step_free)) == NULL + || apol_vector_append(v, r) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + infoflow_result_free(r); + return NULL; + } + r->start_type = start_type; + r->end_type = end_type; + r->length = INT_MAX; + return r; +} + +/** + * Append the rules on an edge to a direct infoflow result. + * + * @param p Policy containing rules. + * @param edge Infoflow edge containing rules. + * @param direction Direction of flow, one of APOL_INFOFLOW_IN, etc. + * @param result Infoflow result to modify. + * + * @return 0 on success, < 0 on error. + */ +static int apol_infoflow_direct_define(const apol_policy_t * p, + const apol_infoflow_edge_t * edge, unsigned int direction, apol_infoflow_result_t * result) +{ + apol_infoflow_step_t *step = NULL; + if (apol_vector_get_size(result->steps) == 0) { + if ((step = calloc(1, sizeof(*step))) == NULL || + (step->rules = apol_vector_create(NULL)) == NULL || apol_vector_append(result->steps, step) < 0) { + apol_infoflow_step_free(step); + ERR(p, "%s", strerror(ENOMEM)); + return -1; + } + step->start_type = result->start_type; + step->end_type = result->end_type; + step->weight = 0; + } else { + step = (apol_infoflow_step_t *) apol_vector_get_element(result->steps, 0); + } + if (apol_vector_cat(step->rules, edge->rules) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + return -1; + } + result->direction |= direction; + //TODO: check that edge->lenght can be safely unsigned + if (edge->length < (int)result->length) { + result->length = edge->length; + } + return 0; +} + +/** + * Given the regular expression compiled into the graph object and a + * type, determine if that regex matches that type or any of the + * type's aliases. + * + * @param p Policy containing type names. + * @param g Graph object containing regex. + * @param type Type to check against. + * + * @return 1 if comparison succeeds, 0 if not, < 0 on error. + */ +static int apol_infoflow_graph_compare(const apol_policy_t * p, apol_infoflow_graph_t * g, const qpol_type_t * type) +{ + const char *type_name; + qpol_iterator_t *alias_iter = NULL; + int compval = 0; + if (g->regex == NULL) { + return 1; + } + if (qpol_type_get_name(p->p, type, &type_name) < 0) { + return -1; + } + if (regexec(g->regex, type_name, 0, NULL, 0) == 0) { + return 1; + } + /* also check for matches against any of target's aliases */ + if (qpol_type_get_alias_iter(p->p, type, &alias_iter) < 0) { + return -1; + } + for (; !qpol_iterator_end(alias_iter); qpol_iterator_next(alias_iter)) { + char *iter_name; + if (qpol_iterator_get_item(alias_iter, (void **)&iter_name) < 0) { + compval = -1; + break; + } + if (regexec(g->regex, iter_name, 0, NULL, 0) == 0) { + compval = 1; + break; + } + } + qpol_iterator_destroy(&alias_iter); + return compval; +} + +/** + * For each result object in vector working_results, append a + * duplicate of it to vector results if (a) the infoflow analysis + * object direction is not BOTH or (b) the result object's direction + * is BOTH. Regardless of success or error, it is safe to destroy + * either vector without concern of double-free()ing things. + * + * @param p Policy handler, for reporting errors. + * @param working_results Vector of infoflow results to check. + * @param direction Direction of search. + * @param results Vector to which append duplicated infoflow results. + * + * @return 0 on success, < 0 on error. + */ +static int apol_infoflow_results_check_both(const apol_policy_t * p, + const apol_vector_t * working_results, unsigned int direction, apol_vector_t * results) +{ + size_t i; + apol_infoflow_result_t *r, *new_r; + for (i = 0; i < apol_vector_get_size(working_results); i++) { + r = (apol_infoflow_result_t *) apol_vector_get_element(working_results, i); + if (direction != APOL_INFOFLOW_BOTH || r->direction == APOL_INFOFLOW_BOTH) { + if ((new_r = calloc(1, sizeof(*new_r))) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + return -1; + } + memcpy(new_r, r, sizeof(*new_r)); + r->steps = NULL; + if (apol_vector_append(results, new_r) < 0) { + infoflow_result_free(new_r); + ERR(p, "%s", strerror(ENOMEM)); + return -1; + } + } + } + return 0; +} + +/** + * Given a start node, an edge, and flow direction, add an infoflow + * results to a vector. If the node on the other end of the edge is + * an attribute, first expand the attribute to its component types. + * If a regular expression is compiled into the infoflow graph, apply + * that regex match against candidate end node types prior to creating + * result nodes. + * + * @param p Policy to analyze. + * @param g Information flow graph to analyze. + * @param start_node Starting node. + * @param edge An edge from start_node. + * @param flow_dir Direction of search, either APOL_INFOFLOW_IN or + * APOL_INFOFLOW_OUT. + * @param results Non-NULL vector to which append infoflow results. + * The caller is responsible for calling apol_infoflow_results_free() + * upon each element afterwards. + * + * @return 0 on success, < 0 on error. + */ +static int apol_infoflow_analysis_direct_expand(const apol_policy_t * p, + apol_infoflow_graph_t * g, + apol_infoflow_node_t * start_node, + apol_infoflow_edge_t * edge, unsigned int flow_dir, apol_vector_t * results) +{ + apol_infoflow_node_t *end_node; + unsigned char isattr; + qpol_iterator_t *iter = NULL; + const qpol_type_t *type; + apol_infoflow_result_t *r; + int retval = -1, compval; + + if (edge->start_node == start_node) { + end_node = edge->end_node; + } else { + end_node = edge->start_node; + } + if (qpol_type_get_isattr(p->p, end_node->type, &isattr) < 0) { + goto cleanup; + } + if (isattr) { + if (qpol_type_get_type_iter(p->p, end_node->type, &iter) < 0) { + goto cleanup; + } + if (qpol_iterator_end(iter)) { + retval = 0; + goto cleanup; + } + } + /* always do this loop once, either if end_node is an attribute or not */ + do { + if (isattr) { + if (qpol_iterator_get_item(iter, (void **)&type) < 0) { + goto cleanup; + } + qpol_iterator_next(iter); + } else { + type = end_node->type; + } + compval = apol_infoflow_graph_compare(p, g, type); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + if ((r = apol_infoflow_direct_get_result(p, results, start_node->type, type)) == NULL || + apol_infoflow_direct_define(p, edge, flow_dir, r) < 0) { + goto cleanup; + } + } while (isattr && !qpol_iterator_end(iter)); + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} + +/** + * Perform a direct information flow analysis upon the given infoflow + * graph. + * + * @param p Policy to analyze. + * @param g Information flow graph to analyze. + * @param start_type Type from which to begin search. + * @param results Non-NULL vector to which append infoflow results. + * The caller is responsible for calling apol_infoflow_results_free() + * upon each element afterwards. + * + * @return 0 on success, < 0 on error. + */ +static int apol_infoflow_analysis_direct(const apol_policy_t * p, + apol_infoflow_graph_t * g, const char *start_type, apol_vector_t * results) +{ + apol_vector_t *nodes = NULL; + size_t i, j; + apol_infoflow_node_t *node; + apol_infoflow_edge_t *edge; + apol_vector_t *working_results = NULL; + int retval = -1; + + if ((nodes = apol_vector_create(NULL)) == NULL || (working_results = apol_vector_create(infoflow_result_free)) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_infoflow_graph_get_nodes_for_type(p, g, start_type, nodes) < 0) { + goto cleanup; + } + + if (g->direction == APOL_INFOFLOW_IN || g->direction == APOL_INFOFLOW_EITHER || g->direction == APOL_INFOFLOW_BOTH) { + for (i = 0; i < apol_vector_get_size(nodes); i++) { + node = (apol_infoflow_node_t *) apol_vector_get_element(nodes, i); + for (j = 0; j < apol_vector_get_size(node->in_edges); j++) { + edge = (apol_infoflow_edge_t *) apol_vector_get_element(node->in_edges, j); + if (apol_infoflow_analysis_direct_expand(p, g, node, edge, APOL_INFOFLOW_IN, working_results) < 0) { + goto cleanup; + } + } + } + } + if (g->direction == APOL_INFOFLOW_OUT || g->direction == APOL_INFOFLOW_EITHER || g->direction == APOL_INFOFLOW_BOTH) { + for (i = 0; i < apol_vector_get_size(nodes); i++) { + node = (apol_infoflow_node_t *) apol_vector_get_element(nodes, i); + for (j = 0; j < apol_vector_get_size(node->out_edges); j++) { + edge = (apol_infoflow_edge_t *) apol_vector_get_element(node->out_edges, j); + if (apol_infoflow_analysis_direct_expand(p, g, node, edge, APOL_INFOFLOW_OUT, working_results) < 0) { + goto cleanup; + } + } + } + } + + if (apol_infoflow_results_check_both(p, working_results, g->direction, results) < 0) { + goto cleanup; + } + + retval = 0; + cleanup: + apol_vector_destroy(&nodes); + apol_vector_destroy(&working_results); + return retval; +} + +/*************** infoflow graph transitive analysis routines ***************/ + +/** + * Prepare an infoflow graph for a transitive analysis by coloring its + * nodes and setting its parent and distance. For the start node + * color it red; for all others color them white. + * + * @param p Policy handler, for reporting errors. + * @param g Infoflow graph to initialize. + * @param start Node from which to begin analysis. + * @param q Queue of apol_infoflow_node_t pointers to which search. + * + * @return 0 on success, < 0 on error. + */ +static int apol_infoflow_graph_trans_init(const apol_policy_t * p, + apol_infoflow_graph_t * g, apol_infoflow_node_t * start, apol_queue_t * q) +{ + size_t i; + apol_infoflow_node_t *node; + for (i = 0; i < apol_vector_get_size(g->nodes); i++) { + node = (apol_infoflow_node_t *) apol_vector_get_element(g->nodes, i); + node->parent = NULL; + if (node == start) { + node->color = APOL_INFOFLOW_COLOR_RED; + node->distance = 0; + if (apol_queue_insert(q, node) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + return -1; + } + } else { + node->color = APOL_INFOFLOW_COLOR_WHITE; + node->distance = INT_MAX; + } + } + return 0; +} + +/** + * Prepare an infoflow graph for furher transitive analysis by + * coloring its nodes and setting its parent and distance. For the + * start node color it grey; for all others color them white. + * + * @param p Policy handler, for reporting errors. + * @param g Infoflow graph to initialize. + * @param start Node from which to begin analysis. + * @param q Queue of apol_infoflow_node_t pointers to which search. + * + * @return 0 on success, < 0 on error. + */ +static int apol_infoflow_graph_trans_further_init(const apol_policy_t * p, + apol_infoflow_graph_t * g, apol_infoflow_node_t * start, apol_queue_t * q) +{ + size_t i; + apol_infoflow_node_t *node; + for (i = 0; i < apol_vector_get_size(g->nodes); i++) { + node = (apol_infoflow_node_t *) apol_vector_get_element(g->nodes, i); + node->parent = NULL; + if (node == start) { + node->color = APOL_INFOFLOW_COLOR_GREY; + node->distance = 0; + if (apol_queue_insert(q, node) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + return -1; + } + } else { + node->color = APOL_INFOFLOW_COLOR_WHITE; + node->distance = -1; + } + } + return 0; +} + +/** + * Given a colored infoflow graph from apol_infoflow_analysis_trans(), + * find the shortest path from the end node to the start node. + * Allocate and return a vector of apol_infoflow_node_t that lists the + * nodes from the end to start. + * + * @param p Policy from which infoflow graph was generated. + * @param g Infoflow graph that has been colored. + * @param start_node Starting node for the path + * @param end_node Ending node to which to find a path. + * @param path Reference to a vector that will be allocated and filled + * with apol_infoflow_node_t pointers from the graph. The path will + * be in reverse order (i.e., from end node to a start node). Upon + * error this will be set to NULL. + * + * @return 0 on success, < 0 on error. + */ +static int apol_infoflow_trans_path(const apol_policy_t * p, + apol_infoflow_graph_t * g, + apol_infoflow_node_t * start_node, apol_infoflow_node_t * end_node, apol_vector_t ** path) +{ + int retval = -1; + apol_infoflow_node_t *next_node = end_node; + if ((*path = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + while (1) { + if (apol_vector_append(*path, next_node) < 0) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + if (next_node == start_node) { + break; + } + if (next_node == NULL || apol_vector_get_size(*path) >= apol_vector_get_size(g->nodes)) { + ERR(p, "%s", "Infinite loop in trans_path."); + errno = EPERM; + goto cleanup; + } + next_node = next_node->parent; + } + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(path); + } + return retval; +} + +/** + * Given a node within an infoflow graph, return the edge that + * connects it to next_node. + * + * @param p Policy handler, for reporting errors. + * @param g Infoflow graph from which to find edge. + * @param node Starting node. + * @param next_node Ending node. + * + * @return Edge connecting node to next_node, or NULL on error. + */ +static apol_infoflow_edge_t *apol_infoflow_trans_find_edge(const apol_policy_t * p, + apol_infoflow_graph_t * g, + apol_infoflow_node_t * node, apol_infoflow_node_t * next_node) +{ + apol_vector_t *v; + apol_infoflow_edge_t *edge; + size_t i; + + if (g->direction == APOL_INFOFLOW_OUT) { + v = node->out_edges; + } else { + v = node->in_edges; + } + for (i = 0; i < apol_vector_get_size(v); i++) { + edge = (apol_infoflow_edge_t *) apol_vector_get_element(v, i); + if (g->direction == APOL_INFOFLOW_OUT) { + if (edge->start_node == node && edge->end_node == next_node) { + return edge; + } + } else { + if (edge->end_node == node && edge->start_node == next_node) { + return edge; + } + + } + } + ERR(p, "%s", "Did not find an edge."); + return NULL; +} + +/** + * Given a path of nodes, define a new infoflow result that represents + * that path. The given path is a list of nodes that must be in + * reverse order (i.e., from end node to start node) and must have at + * least 2 elements within. + * + * @param p Policy handler, for reporting errors. + * @param g Graph from which the node path originated. + * @param path Vector of apol_infoflow_node_t representing an infoflow + * path. + * @param end_type Ending type for the path. + * @param result Reference pointer to where to store result. The + * caller is responsible for calling apol_infoflow_result_free() upon + * the returned value. Upon error this will be set to NULL. + * + * @return 0 on success, < 0 on error. + */ +static int apol_infoflow_trans_define(const apol_policy_t * p, + apol_infoflow_graph_t * g, + apol_vector_t * path, const qpol_type_t * end_type, apol_infoflow_result_t ** result) +{ + apol_infoflow_step_t *step = NULL; + size_t path_len = apol_vector_get_size(path), i; + apol_infoflow_node_t *node, *next_node; + apol_infoflow_edge_t *edge; + int retval = -1, length = 0; + *result = NULL; + + if (((*result) = calloc(1, sizeof(**result))) == NULL || + ((*result)->steps = apol_vector_create_with_capacity(path_len, apol_infoflow_step_free)) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + (*result)->end_type = end_type; + /* build in reverse order because path is from end node to + * start node */ + node = (apol_infoflow_node_t *) apol_vector_get_element(path, path_len - 1); + (*result)->start_type = node->type; + (*result)->direction = g->direction; + for (i = path_len - 1; i > 0; i--, node = next_node) { + next_node = (apol_infoflow_node_t *) apol_vector_get_element(path, i - 1); + edge = apol_infoflow_trans_find_edge(p, g, node, next_node); + if (edge == NULL) { + goto cleanup; + } + length += edge->length; + if ((step = calloc(1, sizeof(*step))) == NULL || + (step->rules = apol_vector_create_from_vector(edge->rules, NULL, NULL, NULL)) == NULL || + apol_vector_append((*result)->steps, step) < 0) { + apol_infoflow_step_free(step); + ERR(p, "%s", strerror(ENOMEM)); + return -1; + } + step->start_type = edge->start_node->type; + step->end_type = edge->end_node->type; + step->weight = APOL_PERMMAP_MAX_WEIGHT - edge->length + 1; + } + (*result)->length = length; + retval = 0; + cleanup: + if (retval != 0) { + infoflow_result_free(*result); + *result = NULL; + } + return retval; +} + +/** + * Compares two apol_infoflow_step_t objects, returning 0 if they have + * the same contents, non-zero or not. This is a callback function to + * apol_vector_compare(). + * + * @param a First apol_infoflow_step_t to compare. + * @param b Other apol_infoflow_step_t to compare. + * @param data Unused. + * + * @return 0 if the steps are the same, non-zero if different. + */ +static int apol_infoflow_trans_step_comp(const void *a, const void *b, void *data __attribute__ ((unused))) +{ + const apol_infoflow_step_t *step_a = (const apol_infoflow_step_t *)a; + const apol_infoflow_step_t *step_b = (const apol_infoflow_step_t *)b; + size_t i; + if (step_a->start_type != step_b->start_type) { + return (int)((char *)step_a->start_type - (char *)step_b->start_type); + } + if (step_a->end_type != step_b->end_type) { + return (int)((char *)step_a->end_type - (char *)step_b->end_type); + } + return apol_vector_compare(step_a->rules, step_b->rules, NULL, NULL, &i); +} + +/** + * Given a path, append to the results vector a new + * apol_infoflow_result object - but only if there is not already a + * result describing the same path. + * + * @param p Policy handler, for reporting errors. + * @param g Infoflow graph to which create results. + * @param path Vector of apol_infoflow_node_t describing a path from + * an end node to a starting node. + * @param end_type Ending type for the path. + * @param results Vector of apol_infoflow_result_t to possibly append + * a new result. + * + * @return 0 on success, < 0 on error. + */ +static int apol_infoflow_trans_append(const apol_policy_t * p, + apol_infoflow_graph_t * g, + apol_vector_t * path, const qpol_type_t * end_type, apol_vector_t * results) +{ + apol_infoflow_result_t *new_r = NULL, *r; + size_t i, j; + int compval, retval = -1; + + if (apol_infoflow_trans_define(p, g, path, end_type, &new_r) < 0) { + goto cleanup; + } + + /* First we look for duplicate paths */ + for (i = 0; i < apol_vector_get_size(results); i++) { + r = (apol_infoflow_result_t *) apol_vector_get_element(results, i); + if (r->end_type != end_type || + r->direction != new_r->direction || apol_vector_get_size(r->steps) != apol_vector_get_size(new_r->steps)) { + break; + } + compval = apol_vector_compare(r->steps, new_r->steps, apol_infoflow_trans_step_comp, NULL, &j); + /* found a dup TODO - make certain all of the object + * class / rules are kept */ + if (compval == 0) { + infoflow_result_free(new_r); + new_r = NULL; + retval = 0; + goto cleanup; + } + } + + /* If we are here the newly built path is unique. */ + if (apol_vector_append(results, new_r) < 0) { + goto cleanup; + } + retval = 0; + cleanup: + if (retval != 0) { + infoflow_result_free(new_r); + } + return retval; +} + +/** + * Given a start and end node, add a trans infoflow results to a + * vector. If a regular expression is compiled into the infoflow + * graph, apply that regex match against candidate end node types + * prior to creating result nodes. + * + * @param p Policy to analyze. + * @param g Information flow graph to analyze. + * @param start_node Starting node. + * @param end_node Ending node. + * @param results Non-NULL vector to which append infoflow result. + * The caller is responsible for calling apol_infoflow_results_free() + * upon each element afterwards. + * + * @return 0 on success (including no result actually added), or < 0 + * on error. + */ +static int apol_infoflow_analysis_trans_expand(const apol_policy_t * p, + apol_infoflow_graph_t * g, + apol_infoflow_node_t * start_node, + apol_infoflow_node_t * end_node, apol_vector_t * results) +{ + unsigned char isattr; + apol_vector_t *path = NULL; + int retval = -1, compval; + + if (qpol_type_get_isattr(p->p, end_node->type, &isattr) < 0) { + goto cleanup; + } + assert(isattr == 0); + if (start_node->type == end_node->type) { + return 0; + } + compval = apol_infoflow_graph_compare(p, g, end_node->type); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + return 0; + } + if (apol_infoflow_trans_path(p, g, start_node, end_node, &path) < 0 || + apol_infoflow_trans_append(p, g, path, end_node->type, results) < 0) { + goto cleanup; + } + retval = 0; + cleanup: + apol_vector_destroy(&path); + return retval; +} + +/** + * Perform a transitive information flow analysis upon the given + * infoflow graph starting from some particular node within the graph. + * + * This is a label correcting shortest path algorithm; see Bertsekas, + * D. P., "A Simple and Fast Label Correcting Algorithm for Shortest + * Paths," Networks, Vol. 23, pp. 703-709, 1993. for more information. + * A label correcting algorithm is needed instead of the more common + * Dijkstra label setting algorithm to correctly handle the the cycles + * that are possible in these graphs. + * + * This algorithm finds the shortest path between a given start node + * and all other nodes in the graph. Any paths that it finds it + * appends to the iflow_transitive_t structure. This is a basic label + * correcting algorithm with 1 optimization. It uses the D'Esopo-Pape + * method for node selection in the node queue. Why is this faster? + * The paper referenced above says "No definitive explanation has been + * given." They have fancy graphs to show that it is faster though + * and the important part is that the worst case isn't much worse that + * N^2 - much better than an n^3 transitive closure. Additionally, + * most normal sparse graphs are significantly better than the worst + * case. + * + * @param p Policy to analyze. + * @param g Information flow graph to analyze. + * @param start Node from which to begin search. + * @param results Non-NULL vector to which append infoflow results. + * The caller is responsible for calling apol_infoflow_results_free() + * upon each element afterwards. + * + * @return 0 on success, < 0 on error. + */ +static int apol_infoflow_analysis_trans_shortest_path(const apol_policy_t * p, + apol_infoflow_graph_t * g, + apol_infoflow_node_t * start, apol_vector_t * results) +{ + apol_vector_t *edge_list; + apol_queue_t *queue = NULL; + apol_infoflow_node_t *node, *cur_node; + apol_infoflow_edge_t *edge; + size_t i; + int retval = -1; + + if ((queue = apol_queue_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_infoflow_graph_trans_init(p, g, start, queue) < 0) { + goto cleanup; + } + + while ((cur_node = apol_queue_remove(queue)) != NULL) { + cur_node->color = APOL_INFOFLOW_COLOR_GREY; + if (g->direction == APOL_INFOFLOW_OUT) { + edge_list = cur_node->out_edges; + } else { + edge_list = cur_node->in_edges; + } + for (i = 0; i < apol_vector_get_size(edge_list); i++) { + edge = (apol_infoflow_edge_t *) apol_vector_get_element(edge_list, i); + if (g->direction == APOL_INFOFLOW_OUT) { + node = edge->end_node; + } else { + node = edge->start_node; + } + if (node == start) { + continue; + } + + if (node->distance > cur_node->distance + edge->length) { + node->distance = cur_node->distance + edge->length; + node->parent = cur_node; + /* If this node has been inserted into + * the queue before insert it at the + * beginning, otherwise it goes to the + * end. See the comment at the + * beginning of the function for + * why. */ + if (node->color != APOL_INFOFLOW_COLOR_RED) { + if (node->color == APOL_INFOFLOW_COLOR_GREY) { + if (apol_queue_push(queue, node) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } else { + if (apol_queue_insert(queue, node) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + node->color = APOL_INFOFLOW_COLOR_RED; + } + } + } + } + + /* Find all of the paths and add them to the results vector */ + for (i = 0; i < apol_vector_get_size(g->nodes); i++) { + cur_node = (apol_infoflow_node_t *) apol_vector_get_element(g->nodes, i); + if (cur_node->parent == NULL || cur_node == start) { + continue; + } + if (apol_infoflow_analysis_trans_expand(p, g, start, cur_node, results) < 0) { + goto cleanup; + } + } + + retval = 0; + cleanup: + apol_queue_destroy(&queue); + return retval; +} + +/** + * Perform a transitive information flow analysis upon the given + * infoflow graph. + * + * @param p Policy to analyze. + * @param g Information flow graph to analyze. + * @param start_type Type from which to begin search. + * @param results Non-NULL vector to which append infoflow results. + * The caller is responsible for calling apol_infoflow_results_free() + * upon each element afterwards. + * + * @return 0 on success, < 0 on error. + */ +static int apol_infoflow_analysis_trans(const apol_policy_t * p, + apol_infoflow_graph_t * g, const char *start_type, apol_vector_t * results) +{ + apol_vector_t *start_nodes = NULL; + apol_infoflow_node_t *start_node; + size_t i; + int retval = -1; + + if (g->direction != APOL_INFOFLOW_IN && g->direction != APOL_INFOFLOW_OUT) { + ERR(p, "%s", strerror(EINVAL)); + goto cleanup; + } + if ((start_nodes = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + if (apol_infoflow_graph_get_nodes_for_type(p, g, start_type, start_nodes) < 0) { + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(start_nodes); i++) { + start_node = (apol_infoflow_node_t *) apol_vector_get_element(start_nodes, i); + if (apol_infoflow_analysis_trans_shortest_path(p, g, start_node, results) < 0) { + goto cleanup; + } + } + retval = 0; + cleanup: + apol_vector_destroy(&start_nodes); + return retval; +} + +/** + * Given a vector, allocate and return a new vector with the elements + * shuffled about. This will make a shallow copy of the original + * vector's elements. + * + * @param p Policy handler, for error reporting. + * @param g Transitive infoflow graph containing PRNG object. + * @param v Vector to shuffle. + * + * @return A newly allocated vector with shuffled elements, or NULL + * upon error. The caller must call apol_vector_destroy() upon the + * returned value. + */ +static apol_vector_t *apol_infoflow_trans_further_shuffle(const apol_policy_t * p, apol_infoflow_graph_t * g, apol_vector_t * v) +{ + size_t i, j, size; + void **deck = NULL, *tmp; + apol_vector_t *new_v = NULL; + int retval = -1; + size = apol_vector_get_size(v); + if ((new_v = apol_vector_create_with_capacity(size, NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + if (size == 0) { + retval = 0; + goto cleanup; + } + if ((deck = malloc(size * sizeof(*deck))) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (i = 0; i < size; i++) { + deck[i] = apol_vector_get_element(v, i); + } + for (i = size - 1; i > 0; i--) { + j = (size_t) ((apol_infoflow_rand(g) / (RAND_MAX + 1.0)) * i); + tmp = deck[i]; + deck[i] = deck[j]; + deck[j] = tmp; + } + for (i = 0; i < size; i++) { + if (apol_vector_append(new_v, deck[i]) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + retval = 0; + cleanup: + free(deck); + if (retval != 0) { + apol_vector_destroy(&new_v); + } + return new_v; +} + +static int apol_infoflow_analysis_trans_further(const apol_policy_t * p, + apol_infoflow_graph_t * g, apol_infoflow_node_t * start, apol_vector_t * results) +{ + apol_vector_t *edge_list = NULL; + apol_queue_t *queue = NULL; + apol_infoflow_node_t *node, *cur_node; + apol_infoflow_edge_t *edge; + size_t i; + int retval = -1; + + if ((queue = apol_queue_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_infoflow_graph_trans_further_init(p, g, start, queue) < 0) { + goto cleanup; + } + + while ((cur_node = apol_queue_remove(queue)) != NULL) { + if (cur_node != start && + apol_vector_get_index(g->further_end, cur_node, NULL, NULL, &i) == 0 && + apol_infoflow_analysis_trans_expand(p, g, start, cur_node, results) < 0) { + goto cleanup; + } + cur_node->color = APOL_INFOFLOW_COLOR_BLACK; + if (g->direction == APOL_INFOFLOW_OUT) { + edge_list = cur_node->out_edges; + } else { + edge_list = cur_node->in_edges; + } + edge_list = apol_infoflow_trans_further_shuffle(p, g, edge_list); + if (edge_list == NULL) { + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(edge_list); i++) { + edge = (apol_infoflow_edge_t *) apol_vector_get_element(edge_list, i); + if (g->direction == APOL_INFOFLOW_OUT) { + node = edge->end_node; + } else { + node = edge->start_node; + } + if (node->color == APOL_INFOFLOW_COLOR_WHITE) { + node->color = APOL_INFOFLOW_COLOR_GREY; + node->distance = cur_node->distance + 1; + node->parent = cur_node; + if (apol_queue_push(queue, node) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + } + apol_vector_destroy(&edge_list); + } + retval = 0; + cleanup: + apol_vector_destroy(&edge_list); + apol_queue_destroy(&queue); + return retval; +} + +/******************** infoflow analysis object routines ********************/ + +int apol_infoflow_analysis_do(const apol_policy_t * p, const apol_infoflow_analysis_t * ia, apol_vector_t ** v, + apol_infoflow_graph_t ** g) +{ + int retval = -1; + if (v != NULL) { + *v = NULL; + } + if (g != NULL) { + *g = NULL; + } + if (p == NULL || ia == NULL || v == NULL || g == NULL || ia->mode == 0 || ia->direction == 0) { + ERR(p, "%s", strerror(EINVAL)); + goto cleanup; + } + if (apol_infoflow_graph_create(p, ia, g) < 0) { + goto cleanup; + } + INFO(p, "%s", "Searching information flow graph."); + retval = apol_infoflow_analysis_do_more(p, *g, ia->type, v); + cleanup: + if (retval != 0) { + apol_infoflow_graph_destroy(g); + } + return retval; +} + +int apol_infoflow_analysis_do_more(const apol_policy_t * p, apol_infoflow_graph_t * g, const char *type, apol_vector_t ** v) +{ + const qpol_type_t *start_type; + int retval = -1; + if (v != NULL) { + *v = NULL; + } + if (p == NULL || g == NULL || type == NULL || v == NULL) { + ERR(p, "%s", strerror(EINVAL)); + goto cleanup; + } + + if (apol_query_get_type(p, type, &start_type) < 0) { + goto cleanup; + } + + if ((*v = apol_vector_create(infoflow_result_free)) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + + if ((g->mode == APOL_INFOFLOW_MODE_DIRECT && + apol_infoflow_analysis_direct(p, g, type, *v) < 0) || + (g->mode == APOL_INFOFLOW_MODE_TRANS && apol_infoflow_analysis_trans(p, g, type, *v) < 0)) { + goto cleanup; + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + return retval; +} + +int apol_infoflow_analysis_trans_further_prepare(const apol_policy_t * p, + apol_infoflow_graph_t * g, const char *start_type, const char *end_type) +{ + const qpol_type_t *stype, *etype; + int retval = -1; + + apol_infoflow_srand(g); + if (apol_query_get_type(p, start_type, &stype) < 0 || apol_query_get_type(p, end_type, &etype) < 0) { + goto cleanup; + } + if (g->mode != APOL_INFOFLOW_MODE_TRANS) { + ERR(p, "%s", "May only perform further infoflow analysis when the graph is transitive."); + goto cleanup; + } + apol_vector_destroy(&g->further_start); + apol_vector_destroy(&g->further_end); + if ((g->further_start = apol_vector_create(NULL)) == NULL || (g->further_end = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + if (apol_infoflow_graph_get_nodes_for_type(p, g, start_type, g->further_start) < 0 || + apol_infoflow_graph_get_nodes_for_type(p, g, end_type, g->further_end) < 0) { + goto cleanup; + } + g->current_start = 0; + retval = 0; + cleanup: + return retval; +} + +int apol_infoflow_analysis_trans_further_next(const apol_policy_t * p, apol_infoflow_graph_t * g, apol_vector_t ** v) +{ + apol_infoflow_node_t *start_node; + int retval = -1; + if (p == NULL || g == NULL || v == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if (*v == NULL) { + *v = apol_vector_create(infoflow_result_free); + } + if (g->further_start == NULL) { + ERR(p, "%s", "Infoflow graph was not prepared yet."); + goto cleanup; + } + start_node = apol_vector_get_element(g->further_start, g->current_start); + if (apol_infoflow_analysis_trans_further(p, g, start_node, *v) < 0) { + goto cleanup; + } + g->current_start++; + if (g->current_start >= apol_vector_get_size(g->further_start)) { + g->current_start = 0; + } + retval = 0; + cleanup: + return retval; +} + +apol_infoflow_analysis_t *apol_infoflow_analysis_create(void) +{ + return calloc(1, sizeof(apol_infoflow_analysis_t)); +} + +void apol_infoflow_analysis_destroy(apol_infoflow_analysis_t ** ia) +{ + if (*ia != NULL) { + free((*ia)->type); + free((*ia)->result); + apol_vector_destroy(&(*ia)->intermed); + apol_vector_destroy(&(*ia)->class_perms); + free(*ia); + *ia = NULL; + } +} + +int apol_infoflow_analysis_set_mode(const apol_policy_t * p, apol_infoflow_analysis_t * ia, unsigned int mode) +{ + switch (mode) { + case APOL_INFOFLOW_MODE_DIRECT: + case APOL_INFOFLOW_MODE_TRANS: + { + ia->mode = mode; + break; + } + default: + { + ERR(p, "%s", strerror(EINVAL)); + return -1; + } + } + return 0; +} + +int apol_infoflow_analysis_set_dir(const apol_policy_t * p, apol_infoflow_analysis_t * ia, unsigned int dir) +{ + switch (dir) { + case APOL_INFOFLOW_IN: + case APOL_INFOFLOW_OUT: + case APOL_INFOFLOW_BOTH: + case APOL_INFOFLOW_EITHER: + { + ia->direction = dir; + break; + } + default: + { + ERR(p, "%s", strerror(EINVAL)); + return -1; + } + } + return 0; +} + +int apol_infoflow_analysis_set_type(const apol_policy_t * p, apol_infoflow_analysis_t * ia, const char *name) +{ + if (name == NULL) { + ERR(p, "%s", strerror(EINVAL)); + return -1; + } + return apol_query_set(p, &ia->type, NULL, name); +} + +static int compare_class_perm_by_class_name(const void *in_op, const void *class_name, void *unused __attribute__ ((unused))) +{ + const apol_obj_perm_t *op = (const apol_obj_perm_t *)in_op; + const char *name = (const char *)class_name; + + return strcmp(apol_obj_perm_get_obj_name(op), name); +} + +int apol_infoflow_analysis_append_intermediate(const apol_policy_t * policy, apol_infoflow_analysis_t * ia, const char *type) +{ + char *tmp = NULL; + if (type == NULL) { + apol_vector_destroy(&ia->intermed); + return 0; + } + if (ia->intermed == NULL && (ia->intermed = apol_vector_create(free)) == NULL) { + ERR(policy, "Error appending type to analysis: %s", strerror(ENOMEM)); + return -1; + } + if ((tmp = strdup(type)) == NULL || apol_vector_append(ia->intermed, tmp) < 0) { + free(tmp); + ERR(policy, "Error appending type to analysis: %s", strerror(ENOMEM)); + return -1; + } + return 0; +} + +int apol_infoflow_analysis_append_class_perm(const apol_policy_t * p, + apol_infoflow_analysis_t * ia, const char *class_name, const char *perm_name) +{ + apol_obj_perm_t *op = NULL; + size_t i; + + if (p == NULL || ia == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if (class_name == NULL) { + apol_vector_destroy(&ia->class_perms); + return 0; + } + if (perm_name == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if (ia->class_perms == NULL && (ia->class_perms = apol_vector_create(apol_obj_perm_free)) == NULL) { + ERR(p, "%s", strerror(errno)); + return -1; + } + + if (apol_vector_get_index(ia->class_perms, (void *)class_name, compare_class_perm_by_class_name, NULL, &i) < 0) { + if (perm_name) { + if ((op = apol_obj_perm_create()) == NULL) { + ERR(p, "%s", strerror(errno)); + return -1; + } + if (apol_obj_perm_set_obj_name(op, class_name) || + apol_obj_perm_append_perm(op, perm_name) || apol_vector_append(ia->class_perms, op)) { + ERR(p, "%s", strerror(errno)); + apol_obj_perm_free(op); + return -1; + } + } else { + return 0; /* nothing to clear; done */ + } + } else { + op = apol_vector_get_element(ia->class_perms, i); + if (apol_obj_perm_append_perm(op, perm_name)) { + ERR(p, "%s", strerror(errno)); + return -1; + } + } + return 0; +} + +int apol_infoflow_analysis_set_min_weight(const apol_policy_t * p + __attribute__ ((unused)), apol_infoflow_analysis_t * ia, int min_weight) +{ + if (min_weight <= 0) { + ia->min_weight = 0; + } else if (min_weight >= APOL_PERMMAP_MAX_WEIGHT) { + ia->min_weight = APOL_PERMMAP_MAX_WEIGHT; + } else { + ia->min_weight = min_weight; + } + return 0; +} + +int apol_infoflow_analysis_set_result_regex(const apol_policy_t * p, apol_infoflow_analysis_t * ia, const char *result) +{ + return apol_query_set(p, &ia->result, NULL, result); +} + +/*************** functions to access infoflow results ***************/ + +unsigned int apol_infoflow_result_get_dir(const apol_infoflow_result_t * result) +{ + if (!result) { + errno = EINVAL; + return 0; + } + return result->direction; +} + +const qpol_type_t *apol_infoflow_result_get_start_type(const apol_infoflow_result_t * result) +{ + if (!result) { + errno = EINVAL; + return NULL; + } + return result->start_type; +} + +const qpol_type_t *apol_infoflow_result_get_end_type(const apol_infoflow_result_t * result) +{ + if (!result) { + errno = EINVAL; + return NULL; + } + return result->end_type; +} + +unsigned int apol_infoflow_result_get_length(const apol_infoflow_result_t * result) +{ + if (!result) { + errno = EINVAL; + return 0; + } + assert(result->length != 0); + return result->length; +} + +const apol_vector_t *apol_infoflow_result_get_steps(const apol_infoflow_result_t * result) +{ + if (!result) { + errno = EINVAL; + return NULL; + } + return result->steps; +} + +const qpol_type_t *apol_infoflow_step_get_start_type(const apol_infoflow_step_t * step) +{ + if (!step) { + errno = EINVAL; + return NULL; + } + return step->start_type; +} + +const qpol_type_t *apol_infoflow_step_get_end_type(const apol_infoflow_step_t * step) +{ + if (!step) { + errno = EINVAL; + return NULL; + } + return step->end_type; +} + +int apol_infoflow_step_get_weight(const apol_infoflow_step_t * step) +{ + if (!step) { + errno = EINVAL; + return -1; + } + return step->weight; +} + +const apol_vector_t *apol_infoflow_step_get_rules(const apol_infoflow_step_t * step) +{ + if (!step) { + errno = EINVAL; + return NULL; + } + return step->rules; +} + +/******************** protected functions ********************/ + +apol_infoflow_result_t *infoflow_result_create_from_infoflow_result(const apol_infoflow_result_t * result) +{ + apol_infoflow_result_t *new_r = NULL; + apol_infoflow_step_t *step, *new_step; + size_t i; + int retval = -1; + + if ((new_r = calloc(1, sizeof(*new_r))) == NULL || + (new_r->steps = apol_vector_create_with_capacity(apol_vector_get_size(result->steps), apol_infoflow_step_free)) == NULL) + { + goto cleanup; + } + new_r->start_type = result->start_type; + new_r->end_type = result->end_type; + new_r->direction = result->direction; + new_r->length = result->length; + for (i = 0; i < apol_vector_get_size(result->steps); i++) { + step = (apol_infoflow_step_t *) apol_vector_get_element(result->steps, i); + if ((new_step = calloc(1, sizeof(*new_step))) == NULL || + (new_step->rules = apol_vector_create_from_vector(step->rules, NULL, NULL, NULL)) == NULL || + apol_vector_append(new_r->steps, new_step) < 0) { + apol_infoflow_step_free(new_step); + goto cleanup; + } + new_step->start_type = step->start_type; + new_step->end_type = step->end_type; + new_step->weight = step->weight; + } + retval = 0; + cleanup: + if (retval != 0) { + infoflow_result_free(new_r); + return NULL; + } + return new_r; +} + +void infoflow_result_free(void *result) +{ + if (result != NULL) { + apol_infoflow_result_t *r = (apol_infoflow_result_t *) result; + apol_vector_destroy(&r->steps); + free(r); + } +} diff --git a/libapol/src/isid-query.c b/libapol/src/isid-query.c new file mode 100644 index 0000000..9cc6211 --- /dev/null +++ b/libapol/src/isid-query.c @@ -0,0 +1,123 @@ +/** + * @file + * + * Provides a way for setools to make queries about initial SIDs + * within a policy. The caller obtains a query object, fills in its + * parameters, and then runs the query; it obtains a vector of + * results. Searches are conjunctive -- all fields of the search + * query must match for a datum to be added to the results query. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" + +#include <errno.h> + +struct apol_isid_query +{ + char *name; + apol_context_t *context; + unsigned int flags; +}; + +/******************** genfscon queries ********************/ + +int apol_isid_get_by_query(const apol_policy_t * p, const apol_isid_query_t * i, apol_vector_t ** v) +{ + qpol_iterator_t *iter; + int retval = -1, retval2; + const qpol_isid_t *isid = NULL; + *v = NULL; + if (qpol_policy_get_isid_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&isid) < 0) { + goto cleanup; + } + if (i != NULL) { + const char *name; + const qpol_context_t *context; + if (qpol_isid_get_name(p->p, isid, &name) < 0 || qpol_isid_get_context(p->p, isid, &context) < 0) { + goto cleanup; + } + retval2 = apol_compare(p, name, i->name, 0, NULL); + if (retval2 < 0) { + goto cleanup; + } else if (retval2 == 0) { + continue; + } + retval2 = apol_compare_context(p, context, i->context, i->flags); + if (retval2 < 0) { + goto cleanup; + } else if (retval2 == 0) { + continue; + } + } + if (apol_vector_append(*v, (void *)isid)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_isid_query_t *apol_isid_query_create(void) +{ + return calloc(1, sizeof(apol_isid_query_t)); +} + +void apol_isid_query_destroy(apol_isid_query_t ** i) +{ + if (*i != NULL) { + free((*i)->name); + apol_context_destroy(&((*i)->context)); + free(*i); + *i = NULL; + } +} + +int apol_isid_query_set_name(const apol_policy_t * p, apol_isid_query_t * i, const char *name) +{ + return apol_query_set(p, &i->name, NULL, name); +} + +int apol_isid_query_set_context(const apol_policy_t * p __attribute__ ((unused)), + apol_isid_query_t * i, apol_context_t * context, unsigned int range_match) +{ + if (i->context != NULL) { + apol_context_destroy(&i->context); + } + i->context = context; + i->flags = (i->flags & ~APOL_QUERY_FLAGS) | range_match; + return 0; +} diff --git a/libapol/src/libapol.map b/libapol/src/libapol.map new file mode 100644 index 0000000..4894374 --- /dev/null +++ b/libapol/src/libapol.map @@ -0,0 +1,86 @@ +VERS_4.0{ + global: + apol_attr_*; + apol_avrule_*; + apol_bool_*; + apol_bst_*; + apol_cat_*; + apol_class_*; + apol_common_*; + apol_cond_*; + apol_config_*; + apol_constraint_*; + apol_context_*; + apol_domain_*; + apol_file_*; + apol_fs_use_*; + apol_genfscon_*; + apol_get_*; + apol_handle_msg; + apol_infoflow_*; + apol_ipv4_addr_render; + apol_ipv6_addr_render; + apol_isid_*; + apol_level_*; + apol_mls_*; + apol_netifcon_*; + apol_nodecon_*; + apol_objclass_to_str; + apol_perm_*; + apol_permmap_*; + apol_policy_*; + apol_policy_path_*; + apol_portcon_*; + apol_protocol_to_str; + apol_qpol_context_render; + apol_range_trans_*; + apol_relabel_*; + apol_role_*; + apol_role_allow_*; + apol_role_trans_*; + apol_rule_type_to_str; + apol_str_*; + apol_syn_*; + apol_terule_*; + apol_type_*; + apol_types_relation_*; + apol_user_*; + apol_validatetrans_*; + apol_vector_*; + libapol_get_version; + local: *; +}; + +VERS_4.1{ + global: + apol_avrule_query_set_all_perms; + apol_bst_inorder_map; + apol_context_convert; + apol_context_create_from_literal; + apol_domain_trans_analysis_append_class; + apol_domain_trans_analysis_append_perm; + apol_mls_level_convert; + apol_mls_level_create_from_literal; + apol_mls_level_is_literal; + apol_mls_level_validate; + apol_mls_range_convert; + apol_mls_range_create_from_literal; + apol_mls_range_create_from_string; + apol_mls_range_is_literal; + apol_nodecon_query_set_protocol; + apol_policy_build_domain_trans_table; + apol_policy_get_permmap; + apol_policy_open_permmap; + apol_policy_reset_domain_trans_table; + apol_policy_save_permmap; + apol_policy_set_permmap; + apol_portcon_query_set_protocol; + apol_str_to_protocol; + apol_str_to_objclass; +} VERS_4.0; + +VERS_4.2{ + global: + apol_permissive_*; + apol_polcap_*; +} VERS_4.1; diff --git a/libapol/src/mls-query.c b/libapol/src/mls-query.c new file mode 100644 index 0000000..6fccc54 --- /dev/null +++ b/libapol/src/mls-query.c @@ -0,0 +1,248 @@ +/** + * @file + * Implementation for querying MLS components. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <assert.h> +#include <errno.h> +#include <regex.h> +#include <stdlib.h> +#include <string.h> + +#include <qpol/iterator.h> + +#include "policy-query-internal.h" +#include <apol/vector.h> + +struct apol_level_query +{ + char *sens_name, *cat_name; + unsigned int flags; + regex_t *sens_regex, *cat_regex; +}; + +struct apol_cat_query +{ + char *cat_name; + unsigned int flags; + regex_t *regex; +}; + +int apol_mls_sens_compare(const apol_policy_t * p, const char *sens1, const char *sens2) +{ + const qpol_level_t *level_datum1, *level_datum2; + if (qpol_policy_get_level_by_name(p->p, sens1, &level_datum1) < 0 || + qpol_policy_get_level_by_name(p->p, sens2, &level_datum2) < 0) { + return -1; + } + if (level_datum1 == level_datum2) { + return 1; + } + return 0; +} + +int apol_mls_cats_compare(const apol_policy_t * p, const char *cat1, const char *cat2) +{ + const qpol_cat_t *qcat1, *qcat2; + if (qpol_policy_get_cat_by_name(p->p, cat1, &qcat1) < 0 || qpol_policy_get_cat_by_name(p->p, cat2, &qcat2) < 0) { + return -1; + } + if (qcat1 == qcat2) { + return 1; + } + return 0; +} + +/******************** level queries ********************/ + +int apol_level_get_by_query(const apol_policy_t * p, apol_level_query_t * l, apol_vector_t ** v) +{ + qpol_iterator_t *iter, *cat_iter = NULL; + int retval = -1, append_level; + *v = NULL; + if (qpol_policy_get_level_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_level_t *level; + unsigned char isalias; + if (qpol_iterator_get_item(iter, (void **)&level) < 0 || qpol_level_get_isalias(p->p, level, &isalias) < 0) { + goto cleanup; + } + if (isalias) { + continue; + } + append_level = 1; + if (l != NULL) { + int compval = apol_compare_level(p, + level, l->sens_name, + l->flags, &(l->sens_regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + if (qpol_level_get_cat_iter(p->p, level, &cat_iter) < 0) { + goto cleanup; + } + append_level = 0; + for (; !qpol_iterator_end(cat_iter); qpol_iterator_next(cat_iter)) { + qpol_cat_t *cat; + if (qpol_iterator_get_item(cat_iter, (void **)&cat) < 0) { + goto cleanup; + } + compval = apol_compare_cat(p, cat, l->cat_name, l->flags, &(l->cat_regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 1) { + append_level = 1; + break; + } + } + qpol_iterator_destroy(&cat_iter); + } + if (append_level && apol_vector_append(*v, level)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&cat_iter); + return retval; +} + +apol_level_query_t *apol_level_query_create(void) +{ + return calloc(1, sizeof(apol_level_query_t)); +} + +void apol_level_query_destroy(apol_level_query_t ** l) +{ + if (*l != NULL) { + free((*l)->sens_name); + free((*l)->cat_name); + apol_regex_destroy(&(*l)->sens_regex); + apol_regex_destroy(&(*l)->cat_regex); + free(*l); + *l = NULL; + } +} + +int apol_level_query_set_sens(const apol_policy_t * p, apol_level_query_t * l, const char *name) +{ + return apol_query_set(p, &l->sens_name, &l->sens_regex, name); +} + +int apol_level_query_set_cat(const apol_policy_t * p, apol_level_query_t * l, const char *name) +{ + return apol_query_set(p, &l->cat_name, &l->cat_regex, name); +} + +int apol_level_query_set_regex(const apol_policy_t * p, apol_level_query_t * l, int is_regex) +{ + return apol_query_set_regex(p, &l->flags, is_regex); +} + +/******************** category queries ********************/ + +int apol_cat_get_by_query(const apol_policy_t * p, apol_cat_query_t * c, apol_vector_t ** v) +{ + qpol_iterator_t *iter; + int retval = -1; + *v = NULL; + if (qpol_policy_get_cat_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_cat_t *cat; + unsigned char isalias; + if (qpol_iterator_get_item(iter, (void **)&cat) < 0 || qpol_cat_get_isalias(p->p, cat, &isalias) < 0) { + goto cleanup; + } + if (isalias) { + continue; + } + if (c != NULL) { + int compval = apol_compare_cat(p, + cat, c->cat_name, + c->flags, &(c->regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + } + if (apol_vector_append(*v, cat)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_cat_query_t *apol_cat_query_create(void) +{ + return calloc(1, sizeof(apol_cat_query_t)); +} + +void apol_cat_query_destroy(apol_cat_query_t ** c) +{ + if (*c != NULL) { + free((*c)->cat_name); + apol_regex_destroy(&(*c)->regex); + free(*c); + *c = NULL; + } +} + +int apol_cat_query_set_cat(const apol_policy_t * p, apol_cat_query_t * c, const char *name) +{ + return apol_query_set(p, &c->cat_name, &c->regex, name); +} + +int apol_cat_query_set_regex(const apol_policy_t * p, apol_cat_query_t * c, int is_regex) +{ + return apol_query_set_regex(p, &c->flags, is_regex); +} diff --git a/libapol/src/mls_level.c b/libapol/src/mls_level.c new file mode 100644 index 0000000..26a1469 --- /dev/null +++ b/libapol/src/mls_level.c @@ -0,0 +1,771 @@ +/** + * @file + * Implementation of apol_mls_level class. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <apol/mls_level.h> + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "policy-query-internal.h" + +#include <qpol/iterator.h> +#include <apol/vector.h> + +struct apol_mls_level +{ + char *sens; + apol_vector_t *cats; // if NULL, then level is incomplete + char *literal_cats; +}; + +/********************* miscellaneous routines *********************/ + +/* Given a category datum and a category name, returns < 0 if a has + * higher value than b, > 0 if b is higher according to the given + * policy. If the two are equal or upon error, return 0. + */ +static int apol_mls_cat_vector_compare(const void *a, const void *b, void *data) +{ + const qpol_cat_t *cat1 = a; + const char *cat2_name = b; + apol_policy_t *p = (apol_policy_t *) data; + const qpol_cat_t *cat2; + uint32_t cat_value1, cat_value2; + if (qpol_policy_get_cat_by_name(p->p, cat2_name, &cat2) < 0) { + return 0; + } + if (qpol_cat_get_value(p->p, cat1, &cat_value1) < 0 || qpol_cat_get_value(p->p, cat2, &cat_value2) < 0) { + return 0; + } + return (cat_value2 - cat_value1); +} + +/** + * Given two category names, returns < 0 if a has higher value than b, + * > 0 if b is higher. The comparison is against the categories' + * values according to the supplied policy. If the two are equal or + * upon error, return 0. + * + * @param a First category name to compare. + * @param b Other name to compare. + * @param data Pointer to a policy to which use for comparison. + * + * @return <0, 0, or >0 if a is less than, equal, or greater than b, + * respectively. + */ +static int apol_mls_cat_name_compare(const void *a, const void *b, void *data) +{ + const char *cat1 = a; + const char *cat2 = b; + apol_policy_t *p = (apol_policy_t *) data; + const qpol_cat_t *qcat1, *qcat2; + uint32_t cat_value1, cat_value2; + if (qpol_policy_get_cat_by_name(p->p, cat1, &qcat1) < 0 || qpol_policy_get_cat_by_name(p->p, cat2, &qcat2) < 0) { + return 0; + } + if (qpol_cat_get_value(p->p, qcat1, &cat_value1) < 0 || qpol_cat_get_value(p->p, qcat2, &cat_value2) < 0) { + return 0; + } + return (cat_value1 - cat_value2); +} + +/********************* level *********************/ + +apol_mls_level_t *apol_mls_level_create(void) +{ + apol_mls_level_t *l; + if ((l = calloc(1, sizeof(*l))) == NULL || (l->cats = apol_vector_create(free)) == NULL) { + apol_mls_level_destroy(&l); + return NULL; + } + return l; +} + +apol_mls_level_t *apol_mls_level_create_from_mls_level(const apol_mls_level_t * level) +{ + apol_mls_level_t *l; + if ((l = calloc(1, sizeof(*l))) == NULL) { + return NULL; + } + if (level != NULL) { + if ((level->sens != NULL) && (l->sens = strdup(level->sens)) == NULL) { + apol_mls_level_destroy(&l); + return NULL; + } + if ((level->cats != NULL) && + (l->cats = apol_vector_create_from_vector(level->cats, apol_str_strdup, NULL, free)) == NULL) { + apol_mls_level_destroy(&l); + return NULL; + } + if ((level->literal_cats != NULL) && (l->literal_cats = strdup(level->literal_cats)) == NULL) { + apol_mls_level_destroy(&l); + return NULL; + } + } + return l; +} + +apol_mls_level_t *apol_mls_level_create_from_string(const apol_policy_t * p, const char *mls_level_string) +{ + if (p == NULL || mls_level_string == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + + apol_mls_level_t *l = apol_mls_level_create_from_literal(mls_level_string); + if (l == NULL) { + ERR(p, "%s", strerror(errno)); + return NULL; + } + + if (apol_mls_level_convert(p, l) < 0) { + apol_mls_level_destroy(&l); + return NULL; + } + free(l->literal_cats); + l->literal_cats = NULL; + return l; +} + +apol_mls_level_t *apol_mls_level_create_from_literal(const char *mls_level_string) +{ + apol_mls_level_t *l; + char *colon; + if (mls_level_string == NULL) { + errno = EINVAL; + return NULL; + } + if ((l = calloc(1, sizeof(*l))) == NULL) { + return NULL; + } + if ((colon = strchr(mls_level_string, ':')) != NULL) { + // both a sensitivity and 1 or more categories + if (colon == mls_level_string) { + apol_mls_level_destroy(&l); + errno = EINVAL; + return NULL; + } + if ((l->sens = strndup(mls_level_string, colon - mls_level_string)) == NULL) { + apol_mls_level_destroy(&l); + return NULL; + } + // store everything after the colon as the category string + if ((l->literal_cats = strdup(colon + 1)) == NULL) { + apol_mls_level_destroy(&l); + return NULL; + } + apol_str_trim(l->literal_cats); + } else { + // no category, just a sensitivity + if ((l->sens = strdup(mls_level_string)) == NULL || (l->literal_cats = strdup("")) == NULL) { + apol_mls_level_destroy(&l); + return NULL; + } + } + apol_str_trim(l->sens); + return l; +} + +apol_mls_level_t *apol_mls_level_create_from_qpol_mls_level(const apol_policy_t * p, const qpol_mls_level_t * qpol_level) +{ + apol_mls_level_t *lvl = NULL; + qpol_iterator_t *iter = NULL; + const qpol_cat_t *tmp_cat = NULL; + const char *tmp = NULL; + int error = 0; + + if (!p || !qpol_level) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + goto err; + } + + if ((lvl = apol_mls_level_create()) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + if (qpol_mls_level_get_sens_name(p->p, qpol_level, &tmp) || qpol_mls_level_get_cat_iter(p->p, qpol_level, &iter)) { + error = errno; + goto err; + } + if (apol_mls_level_set_sens(p, lvl, tmp) < 0) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&tmp_cat) < 0 || qpol_cat_get_name(p->p, tmp_cat, &tmp) < 0) { + error = errno; + goto err; + } + if (apol_mls_level_append_cats(p, lvl, tmp) < 0) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + } + + qpol_iterator_destroy(&iter); + return lvl; + + err: + apol_mls_level_destroy(&lvl); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; +} + +apol_mls_level_t *apol_mls_level_create_from_qpol_level_datum(const apol_policy_t * p, const qpol_level_t * qpol_level) +{ + apol_mls_level_t *lvl = NULL; + qpol_iterator_t *iter = NULL; + const qpol_cat_t *tmp_cat = NULL; + const char *tmp = NULL; + int error = 0; + + if (!p || !qpol_level) { + errno = EINVAL; + return NULL; + } + + if ((lvl = apol_mls_level_create()) == NULL) { + ERR(p, "%s", strerror(error)); + return NULL; + } + if (qpol_level_get_name(p->p, qpol_level, &tmp)) { + error = errno; + goto err; + } + if ((lvl->sens = strdup(tmp)) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + + if (qpol_level_get_cat_iter(p->p, qpol_level, &iter)) { + error = errno; + goto err; + } + + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&tmp_cat)) { + error = errno; + goto err; + } + if (qpol_cat_get_name(p->p, tmp_cat, &tmp)) { + error = errno; + goto err; + } + if (apol_mls_level_append_cats(p, lvl, tmp)) { + error = errno; + goto err; + } + } + qpol_iterator_destroy(&iter); + return lvl; + + err: + apol_mls_level_destroy(&lvl); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; +} + +static void mls_level_free(void *level) +{ + if (level != NULL) { + apol_mls_level_t *l = level; + free(l->sens); + apol_vector_destroy(&l->cats); + free(l->literal_cats); + free(l); + } +} + +void apol_mls_level_destroy(apol_mls_level_t ** level) +{ + if (!level || !(*level)) + return; + mls_level_free(*level); + *level = NULL; +} + +int apol_mls_level_set_sens(const apol_policy_t * p, apol_mls_level_t * level, const char *sens) +{ + if (!level) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + return apol_query_set(p, &level->sens, NULL, sens); +} + +const char *apol_mls_level_get_sens(const apol_mls_level_t * level) +{ + if (!level) { + errno = EINVAL; + return NULL; + } + return level->sens; +} + +int apol_mls_level_append_cats(const apol_policy_t * p, apol_mls_level_t * level, const char *cats) +{ + char *new_cat = NULL; + if (!level || !cats || level->cats == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + if (level->cats == NULL && (level->cats = apol_vector_create(free)) == NULL) { + ERR(p, "%s", strerror(errno)); + return -1; + } + if ((new_cat = strdup(cats)) == NULL || apol_vector_append(level->cats, (void *)new_cat) < 0) { + ERR(p, "%s", strerror(errno)); + free(new_cat); + return -1; + } + apol_vector_sort(level->cats, apol_str_strcmp, NULL); + return 0; +} + +const apol_vector_t *apol_mls_level_get_cats(const apol_mls_level_t * level) +{ + if (!level || level->cats == NULL) { + errno = EINVAL; + return NULL; + } + return level->cats; +} + +int apol_mls_level_compare(const apol_policy_t * p, const apol_mls_level_t * l1, const apol_mls_level_t * l2) +{ + const qpol_level_t *level_datum1, *level_datum2; + int level1_sens, level2_sens, sens_cmp; + size_t l1_size, l2_size, i, j; + int m_list, ucat = 0; + apol_vector_t *cat_list_master, *cat_list_subset; + if (l2 == NULL) { + return APOL_MLS_EQ; + } + if ((l1 != NULL && l1->cats == NULL) || (l2->cats == NULL)) { + errno = EINVAL; + return -1; + } + if (qpol_policy_get_level_by_name(p->p, l1->sens, &level_datum1) < 0 || + qpol_policy_get_level_by_name(p->p, l2->sens, &level_datum2) < 0) { + return -1; + } + + /* compare the level's senstitivity value */ + if (qpol_level_get_value(p->p, level_datum1, (uint32_t *) (&level1_sens)) < 0 || + qpol_level_get_value(p->p, level_datum2, (uint32_t *) (&level2_sens)) < 0) { + return -1; + } + sens_cmp = level1_sens - level2_sens; + + /* determine if all the categories in one level are in the other set */ + l1_size = apol_vector_get_size(l1->cats); + l2_size = apol_vector_get_size(l2->cats); + if (l1_size < l2_size) { + m_list = 2; + cat_list_master = l2->cats; + cat_list_subset = l1->cats; + } else { + m_list = 1; + cat_list_master = l1->cats; + cat_list_subset = l2->cats; + } + for (i = 0; i < apol_vector_get_size(cat_list_subset); i++) { + char *cat = (char *)apol_vector_get_element(cat_list_subset, i); + if (apol_vector_get_index(cat_list_master, cat, apol_mls_cat_name_compare, (void *)p, &j) < 0) { + ucat = 1; + break; + } + } + + if (!sens_cmp && !ucat && l1_size == l2_size) + return APOL_MLS_EQ; + if (sens_cmp >= 0 && m_list == 1 && !ucat) + return APOL_MLS_DOM; + if (sens_cmp <= 0 && (m_list == 2 || l1_size == l2_size) && !ucat) + return APOL_MLS_DOMBY; + return APOL_MLS_INCOMP; +} + +int apol_mls_level_validate(const apol_policy_t * p, const apol_mls_level_t * level) +{ + const qpol_level_t *level_datum; + qpol_iterator_t *iter = NULL; + apol_vector_t *cat_vector; + int retval = -1; + size_t i, j; + + if (p == NULL || level == NULL || level->cats == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if (level->sens == NULL) { + return 0; + } + if (qpol_policy_get_level_by_name(p->p, level->sens, &level_datum) < 0 || + qpol_level_get_cat_iter(p->p, level_datum, &iter) < 0) { + return -1; + } + if ((cat_vector = apol_vector_create_from_iter(iter, NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + + for (i = 0; i < apol_vector_get_size(level->cats); i++) { + char *cat_name = (char *)apol_vector_get_element(level->cats, i); + if (apol_vector_get_index(cat_vector, cat_name, apol_mls_cat_vector_compare, (void *)p, &j) < 0) { + retval = 0; + goto cleanup; + } + } + + retval = 1; + cleanup: + qpol_iterator_destroy(&iter); + apol_vector_destroy(&cat_vector); + return retval; +} + +char *apol_mls_level_render(const apol_policy_t * p, const apol_mls_level_t * level) +{ + char *rt = NULL; + const char *name = NULL, *sens_name = NULL, *cat_name = NULL; + char *retval = NULL; + int cur; + const qpol_cat_t *cur_cat = NULL, *next_cat = NULL; + uint32_t cur_cat_val, next_cat_val, far_cat_val; + apol_vector_t *cats = NULL; + size_t sz = 0, n_cats = 0, i; + + if (!level || (p == NULL && level->cats != NULL)) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + goto cleanup; + } + + sens_name = level->sens; + if (!sens_name) + goto cleanup; + if (apol_str_append(&rt, &sz, sens_name)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + + if (level->cats != NULL) { + if ((cats = apol_vector_create_from_vector(level->cats, apol_str_strdup, NULL, free)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + n_cats = apol_vector_get_size(cats); + } + if (n_cats == 0) { + if (level->literal_cats != NULL && level->literal_cats[0] != '\0') { + if (apol_str_appendf(&rt, &sz, ":%s", level->literal_cats)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + + } + retval = rt; + goto cleanup; + } + apol_vector_sort(cats, apol_mls_cat_name_compare, (void *)p); + + cat_name = (char *)apol_vector_get_element(cats, 0); + if (!cat_name) + goto cleanup; + + if (apol_str_appendf(&rt, &sz, ":%s", cat_name)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + cur = 0; /* current value to compare with cat[i] */ + for (i = 1; i < n_cats; i++) { /* we've already appended the first category */ + /* get the value of cats[cur] */ + cat_name = (char *)apol_vector_get_element(cats, cur); + if (qpol_policy_get_cat_by_name(p->p, cat_name, &cur_cat)) + goto cleanup; + if (qpol_cat_get_value(p->p, cur_cat, &cur_cat_val)) + goto cleanup; + + /* get the value of cats[i] */ + cat_name = (char *)apol_vector_get_element(cats, i); + if (qpol_policy_get_cat_by_name(p->p, cat_name, &next_cat)) + goto cleanup; + if (qpol_cat_get_value(p->p, next_cat, &next_cat_val)) + goto cleanup; + + if (next_cat_val == cur_cat_val + 1) { + if (i + 1 == n_cats) { /* last category is next; append "." */ + if (qpol_cat_get_name(p->p, next_cat, &name)) + goto cleanup; + if (apol_str_appendf(&rt, &sz, ".%s", name)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + cur = i; + } else { + const qpol_cat_t *far_cat = NULL; /* category 2 in front of cur */ + cat_name = (char *)apol_vector_get_element(cats, i + 1); + if (qpol_policy_get_cat_by_name(p->p, cat_name, &far_cat)) + goto cleanup; + if (qpol_cat_get_value(p->p, far_cat, &far_cat_val)) + goto cleanup; + if (far_cat_val == cur_cat_val + 2) { + cur++; + } else { /* far_cat isn't consecutive wrt cur/next_cat; append it */ + if (qpol_cat_get_name(p->p, next_cat, &name)) + goto cleanup; + if (apol_str_appendf(&rt, &sz, ".%s", name)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + cur = i; + } + } + } else { /* next_cat isn't consecutive to cur_cat; append it */ + if (qpol_cat_get_name(p->p, next_cat, &name)) + goto cleanup; + if (apol_str_appendf(&rt, &sz, ", %s", name)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + cur = i; + } + } + + retval = rt; + cleanup: + apol_vector_destroy(&cats); + if (retval != rt) { + free(rt); + } + return retval; +} + +int apol_mls_level_convert(const apol_policy_t * p, apol_mls_level_t * level) +{ + const char *tmp, *cat_name; + char **tokens = NULL, *next = NULL; + size_t num_tokens = 1, i; + qpol_iterator_t *iter = NULL; + const qpol_level_t *sens = NULL; + const qpol_cat_t *cat1 = NULL, *cat2 = NULL, *tmp_cat = NULL; + uint32_t val1 = 0, val2 = 0, tmp_val = 0; + unsigned char tmp_isalias = 0; + + int error = 0; + if (p == NULL || level == NULL || level->literal_cats == NULL) { + error = EINVAL; + ERR(p, "%s", strerror(error)); + goto err; + } + + apol_vector_destroy(&level->cats); + if (level->literal_cats[0] == '\0') { + if ((level->cats = apol_vector_create_with_capacity(1, free)) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + return 0; + } + + for (tmp = level->literal_cats; *tmp; tmp++) { + if ((next = strchr(tmp, ','))) { + tmp = next; + num_tokens++; + } + } + tokens = calloc(num_tokens, sizeof(char *)); + if (!tokens) { + error = errno; + ERR(p, "%s", strerror(ENOMEM)); + goto err; + } + if ((level->cats = apol_vector_create_with_capacity(num_tokens, free)) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + + for (tmp = level->literal_cats, i = 0; *tmp && i < num_tokens; tmp++) { + if (isspace(*tmp)) + continue; + next = strchr(tmp, ','); + if (next) { + tokens[i] = strndup(tmp, next - tmp); + if (!tokens[i]) { + error = errno; + goto err; + } + tmp = next; + next = NULL; + i++; + } else { + tokens[i] = strdup(tmp); + if (!tokens[i]) { + error = errno; + ERR(p, "%s", strerror(ENOMEM)); + goto err; + } + i++; + if (i != num_tokens) { + error = EIO; + goto err; + } + } + } + + if (qpol_policy_get_level_by_name(p->p, level->sens, &sens)) { + error = errno; + goto err; + } + + for (i = 0; i < num_tokens; i++) { + next = strchr(tokens[i], '.'); + if (next) { + *next = '\0'; + next++; + + /* get end points of cat range */ + if (qpol_policy_get_cat_by_name(p->p, tokens[i], &cat1)) { + error = errno; + goto err; + } + if (qpol_policy_get_cat_by_name(p->p, next, &cat2)) { + error = errno; + goto err; + } + + /* get end point values */ + if (qpol_cat_get_value(p->p, cat1, &val1)) { + error = errno; + goto err; + } + if (qpol_cat_get_value(p->p, cat2, &val2)) { + error = errno; + goto err; + } + if (val1 >= val2) { + error = EINVAL; + ERR(p, "%s", strerror(error)); + goto err; + } + if (apol_mls_level_append_cats(p, level, tokens[i])) { + error = errno; + goto err; + } + if (qpol_policy_get_cat_iter(p->p, &iter)) { + error = errno; + goto err; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&tmp_cat)) { + error = errno; + goto err; + } + if (qpol_cat_get_isalias(p->p, tmp_cat, &tmp_isalias)) { + error = errno; + goto err; + } + if (tmp_isalias) + continue; + if (qpol_cat_get_value(p->p, tmp_cat, &tmp_val)) { + error = errno; + goto err; + } + if (tmp_val > val1 && tmp_val < val2) { + if (qpol_cat_get_name(p->p, tmp_cat, &cat_name)) { + error = errno; + goto err; + } + if (apol_mls_level_append_cats(p, level, cat_name)) { + error = errno; + goto err; + } + } + } + if (apol_mls_level_append_cats(p, level, next)) { + error = errno; + goto err; + } + } else { + if (qpol_policy_get_cat_by_name(p->p, tokens[i], &cat1)) { + error = errno; + goto err; + } + if (apol_mls_level_append_cats(p, level, tokens[i])) { + error = errno; + goto err; + } + } + } + + if (tokens) { + for (i = 0; i < num_tokens; i++) + free(tokens[i]); + free(tokens); + } + + qpol_iterator_destroy(&iter); + return 0; + + err: + if (tokens) { + for (i = 0; i < num_tokens; i++) + free(tokens[i]); + free(tokens); + } + qpol_iterator_destroy(&iter); + errno = error; + return -1; +} + +int apol_mls_level_is_literal(const apol_mls_level_t * level) +{ + if (level == NULL) { + return -1; + } + if (level->literal_cats != NULL) { + return 1; + } + return 0; +} diff --git a/libapol/src/mls_range.c b/libapol/src/mls_range.c new file mode 100644 index 0000000..cefe8ac --- /dev/null +++ b/libapol/src/mls_range.c @@ -0,0 +1,641 @@ +/** + * @file + * Implementation of apol_mls_range class. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <apol/mls_range.h> + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "policy-query-internal.h" + +#include <qpol/iterator.h> +#include <apol/vector.h> + +struct apol_mls_range +{ + apol_mls_level_t *low, *high; +}; + +apol_mls_range_t *apol_mls_range_create(void) +{ + return calloc(1, sizeof(apol_mls_range_t)); +} + +apol_mls_range_t *apol_mls_range_create_from_mls_range(const apol_mls_range_t * range) +{ + apol_mls_range_t *r; + if ((r = apol_mls_range_create()) == NULL) { + return NULL; + } + if (range != NULL && + ((r->low = apol_mls_level_create_from_mls_level(range->low)) == NULL || + (r->high = apol_mls_level_create_from_mls_level(range->high)) == NULL)) { + apol_mls_range_destroy(&r); + return NULL; + } + return r; +} + +apol_mls_range_t *apol_mls_range_create_from_string(const apol_policy_t * p, const char *mls_range_string) +{ + if (p == NULL || mls_range_string == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + + apol_mls_range_t *r = apol_mls_range_create(); + if (r == NULL) { + ERR(p, "%s", strerror(errno)); + return NULL; + } + char *dash; + if ((dash = strchr(mls_range_string, '-')) == NULL) { + // just a low level + apol_mls_level_t *l = apol_mls_level_create_from_string(p, mls_range_string); + if (l == NULL) { + ERR(p, "%s", strerror(errno)); + apol_mls_range_destroy(&r); + return NULL; + } + r->low = l; + } else { + // both a low and a high level + if (dash == mls_range_string) { + apol_mls_range_destroy(&r); + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + char *s = strndup(mls_range_string, dash - mls_range_string); + if (s == NULL) { + ERR(p, "%s", strerror(errno)); + apol_mls_range_destroy(&r); + return NULL; + } + apol_mls_level_t *l = apol_mls_level_create_from_string(p, s); + if (l == NULL) { + ERR(p, "%s", strerror(errno)); + apol_mls_range_destroy(&r); + free(s); + return NULL; + } + r->low = l; + free(s); + l = NULL; + + if ((l = apol_mls_level_create_from_string(p, dash + 1)) == NULL) { + ERR(p, "%s", strerror(errno)); + apol_mls_range_destroy(&r); + return NULL; + } + r->high = l; + } + + if (apol_mls_range_validate(p, r) <= 0) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + apol_mls_range_destroy(&r); + return NULL; + } + return r; +} + +apol_mls_range_t *apol_mls_range_create_from_literal(const char *mls_range_string) +{ + if (mls_range_string == NULL) { + errno = EINVAL; + return NULL; + } + + apol_mls_range_t *r = apol_mls_range_create(); + if (r == NULL) { + return NULL; + } + char *dash; + if ((dash = strchr(mls_range_string, '-')) == NULL) { + // just a low level + apol_mls_level_t *l = apol_mls_level_create_from_literal(mls_range_string); + if (l == NULL) { + apol_mls_range_destroy(&r); + return NULL; + } + r->low = l; + } else { + // both a low and a high level + if (dash == mls_range_string) { + apol_mls_range_destroy(&r); + errno = EINVAL; + return NULL; + } + char *s = strndup(mls_range_string, dash - mls_range_string); + if (s == NULL) { + apol_mls_range_destroy(&r); + return NULL; + } + apol_mls_level_t *l = apol_mls_level_create_from_literal(s); + if (l == NULL) { + apol_mls_range_destroy(&r); + free(s); + return NULL; + } + r->low = l; + free(s); + l = NULL; + + if ((l = apol_mls_level_create_from_literal(dash + 1)) == NULL) { + apol_mls_range_destroy(&r); + return NULL; + } + r->high = l; + } + return r; +} + +apol_mls_range_t *apol_mls_range_create_from_qpol_mls_range(const apol_policy_t * p, const qpol_mls_range_t * qpol_range) +{ + apol_mls_range_t *apol_range = NULL; + const qpol_mls_level_t *tmp = NULL; + apol_mls_level_t *tmp_lvl = NULL; + int error = 0; + + if (!p || !qpol_range) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + + apol_range = calloc(1, sizeof(apol_mls_range_t)); + if (!apol_range) { + ERR(p, "%s", strerror(ENOMEM)); + return NULL; + } + + /* low */ + if (qpol_mls_range_get_low_level(p->p, qpol_range, &tmp) || + !(tmp_lvl = apol_mls_level_create_from_qpol_mls_level(p, tmp)) || apol_mls_range_set_low(p, apol_range, tmp_lvl)) { + error = errno; + apol_mls_level_destroy(&tmp_lvl); + goto err; + } + tmp_lvl = NULL; + + /* high */ + if (qpol_mls_range_get_high_level(p->p, qpol_range, &tmp) || + !(tmp_lvl = apol_mls_level_create_from_qpol_mls_level(p, tmp)) || apol_mls_range_set_high(p, apol_range, tmp_lvl)) { + error = errno; + apol_mls_level_destroy(&tmp_lvl); + goto err; + } + + return apol_range; + + err: + apol_mls_range_destroy(&apol_range); + errno = error; + return NULL; +} + +void apol_mls_range_destroy(apol_mls_range_t ** range) +{ + if (!range || !(*range)) + return; + + if ((*range)->low != (*range)->high) { + apol_mls_level_destroy(&((*range)->high)); + } + apol_mls_level_destroy(&((*range)->low)); + free(*range); + *range = NULL; +} + +int apol_mls_range_set_low(const apol_policy_t * p, apol_mls_range_t * range, apol_mls_level_t * level) +{ + if (!range) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if (range->low != level) { + apol_mls_level_destroy(&(range->low)); + range->low = level; + } + return 0; +} + +int apol_mls_range_set_high(const apol_policy_t * p, apol_mls_range_t * range, apol_mls_level_t * level) +{ + if (!range) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + if (range->high != level) { + if (range->low != range->high) { + apol_mls_level_destroy(&(range->high)); + } + range->high = level; + } + return 0; +} + +const apol_mls_level_t *apol_mls_range_get_low(const apol_mls_range_t * range) +{ + if (!range) { + errno = EINVAL; + return NULL; + } + return range->low; +} + +const apol_mls_level_t *apol_mls_range_get_high(const apol_mls_range_t * range) +{ + if (!range) { + errno = EINVAL; + return NULL; + } + return range->high; +} + +int apol_mls_range_compare(const apol_policy_t * p, const apol_mls_range_t * target, const apol_mls_range_t * search, + unsigned int range_compare_type) +{ + int ans1 = -1, ans2 = -1; + if (search == NULL) { + return 1; + } + if (p == NULL || target == NULL || target->low == NULL || search->low == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + /* FIX ME: intersect does not work */ + if ((range_compare_type & APOL_QUERY_SUB) || (range_compare_type & APOL_QUERY_INTERSECT)) { + ans1 = apol_mls_range_contain_subrange(p, target, search); + if (ans1 < 0) { + return -1; + } + } + if ((range_compare_type & APOL_QUERY_SUPER) || (range_compare_type & APOL_QUERY_INTERSECT)) { + ans2 = apol_mls_range_contain_subrange(p, search, target); + if (ans2 < 0) { + return -1; + } + } + /* EXACT has to come first because its bits are both SUB and SUPER */ + if ((range_compare_type & APOL_QUERY_EXACT) == APOL_QUERY_EXACT) { + return (ans1 && ans2); + } else if (range_compare_type & APOL_QUERY_SUB) { + return ans1; + } else if (range_compare_type & APOL_QUERY_SUPER) { + return ans2; + } else if (range_compare_type & APOL_QUERY_INTERSECT) { + return (ans1 || ans2); + } + ERR(p, "%s", "Invalid range compare type argument."); + errno = EINVAL; + return -1; +} + +static int apol_mls_range_does_include_level(const apol_policy_t * p, const apol_mls_range_t * range, + const apol_mls_level_t * level) +{ + int high_cmp = -1, low_cmp = -1; + + if (range->low != range->high) { + low_cmp = apol_mls_level_compare(p, range->low, level); + if (low_cmp < 0) { + return -1; + } + } + const apol_mls_level_t *high_level = (range->high != NULL ? range->high : range->low); + high_cmp = apol_mls_level_compare(p, high_level, level); + if (high_cmp < 0) { + return -1; + } + + if (high_cmp == APOL_MLS_EQ || high_cmp == APOL_MLS_DOM) { + if ((low_cmp == APOL_MLS_EQ || low_cmp == APOL_MLS_DOMBY) && range->low != high_level) { + return 1; + } else if (range->low == high_level) { + return apol_mls_sens_compare(p, apol_mls_level_get_sens(range->low), apol_mls_level_get_sens(level)); + } + } + + return 0; +} + +int apol_mls_range_contain_subrange(const apol_policy_t * p, const apol_mls_range_t * range, const apol_mls_range_t * subrange) +{ + if (p == NULL || apol_mls_range_validate(p, subrange) != 1) { + ERR(p, "%s", strerror(EINVAL)); + return -1; + } + /* parent range validity will be checked via + * apol_mls_range_include_level() */ + + if (apol_mls_range_does_include_level(p, range, subrange->low)) { + if (subrange->high == NULL || apol_mls_range_does_include_level(p, range, subrange->high)) { + return 1; + } + } + return 0; +} + +int apol_mls_range_validate(const apol_policy_t * p, const apol_mls_range_t * range) +{ + int retv; + + if (p == NULL || range == NULL || range->low == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + if ((retv = apol_mls_level_validate(p, range->low)) != 1) { + return retv; + } + + if (range->high == NULL) { + return retv; + } + if (range->high != range->low && (retv = apol_mls_level_validate(p, range->high)) != 1) { + return retv; + } + + /* both low and high levels exist, so now check that high + * dominates low */ + retv = apol_mls_level_compare(p, range->low, range->high); + if (retv < 0) { + return -1; + } else if (retv != APOL_MLS_EQ && retv != APOL_MLS_DOMBY) { + return 0; + } + + return 1; +} + +static int mls_range_comp(const void *a, const void *b, void *data) +{ + const apol_mls_level_t *l1 = a; + const apol_mls_level_t *l2 = b; + qpol_policy_t *q = (qpol_policy_t *) data; + const qpol_level_t *l; + uint32_t low_value, high_value; + qpol_policy_get_level_by_name(q, apol_mls_level_get_sens(l1), &l); + qpol_level_get_value(q, l, &low_value); + qpol_policy_get_level_by_name(q, apol_mls_level_get_sens(l2), &l); + qpol_level_get_value(q, l, &high_value); + assert(low_value != 0 && high_value != 0); + return low_value - high_value; +} + +static int mls_level_name_to_cat_comp(const void *a, const void *b, void *data) +{ + const qpol_cat_t *cat = a; + const char *name = (const char *)b; + qpol_policy_t *q = (qpol_policy_t *) data; + const char *cat_name = ""; + qpol_cat_get_name(q, cat, &cat_name); + return strcmp(name, cat_name); +} + +static void mls_level_free(void *elem) +{ + apol_mls_level_t *level = elem; + apol_mls_level_destroy(&level); +} + +apol_vector_t *apol_mls_range_get_levels(const apol_policy_t * p, const apol_mls_range_t * range) +{ + qpol_policy_t *q = apol_policy_get_qpol(p); + apol_vector_t *v = NULL, *catv = NULL; + const qpol_level_t *l; + uint32_t low_value, high_value, value; + int error = 0; + qpol_iterator_t *iter = NULL, *catiter = NULL; + + if (p == NULL || range == NULL || range->low == NULL) { + error = EINVAL; + ERR(p, "%s", strerror(error)); + goto err; + } + apol_mls_level_t *low_level, *high_level; + low_level = range->low; + if (range->high == NULL) { + high_level = low_level; + } else { + high_level = range->high; + } + if (qpol_policy_get_level_by_name(q, apol_mls_level_get_sens(low_level), &l) < 0 || + qpol_level_get_value(q, l, &low_value) < 0) { + error = errno; + goto err; + } + if (qpol_policy_get_level_by_name(q, apol_mls_level_get_sens(high_level), &l) < 0 || + qpol_level_get_value(q, l, &high_value) < 0) { + error = errno; + goto err; + } + assert(low_value <= high_value); + if ((v = apol_vector_create(mls_level_free)) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto err; + } + if (qpol_policy_get_level_iter(q, &iter) < 0) { + error = errno; + goto err; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + const char *name; + apol_mls_level_t *ml; + if (qpol_iterator_get_item(iter, (void **)&l) < 0 || + qpol_level_get_value(q, l, &value) < 0 || qpol_level_get_name(q, l, &name) < 0) { + error = errno; + goto err; + } + if (value < low_value || value > high_value) { + continue; + } + if ((ml = apol_mls_level_create()) == NULL || (apol_mls_level_set_sens(p, ml, name) < 0)) { + error = errno; + apol_mls_level_destroy(&ml); + ERR(p, "%s", strerror(error)); + goto err; + } + + if (qpol_level_get_cat_iter(q, l, &catiter) < 0 || (catv = apol_vector_create_from_iter(catiter, NULL)) == NULL) { + error = errno; + goto err; + } + + const apol_vector_t *high_cats = apol_mls_level_get_cats(high_level); + for (size_t i = 0; i < apol_vector_get_size(high_cats); i++) { + char *cat_name = apol_vector_get_element(high_cats, i); + + size_t j; + /* do not add categories that are not members of + the level */ + if (apol_vector_get_index(catv, cat_name, mls_level_name_to_cat_comp, q, &j) < 0) { + /* this category is not legal under the given policy */ + continue; + } + if (apol_mls_level_append_cats(p, ml, cat_name) < 0) { + error = errno; + apol_mls_level_destroy(&ml); + ERR(p, "%s", strerror(error)); + goto err; + } + } + + qpol_iterator_destroy(&catiter); + apol_vector_destroy(&catv); + + if (apol_vector_append(v, ml) < 0) { + error = errno; + apol_mls_level_destroy(&ml); + ERR(p, "%s", strerror(error)); + goto err; + } + } + apol_vector_sort(v, mls_range_comp, q); + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&catiter); + apol_vector_destroy(&catv); + return v; + err: + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&catiter); + apol_vector_destroy(&v); + apol_vector_destroy(&catv); + errno = error; + return NULL; +} + +char *apol_mls_range_render(const apol_policy_t * p, const apol_mls_range_t * range) +{ + char *rt = NULL, *retval = NULL; + char *sub_str = NULL; + int retv; + size_t sz = 0; + + if (!range || range->low == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + goto cleanup; + } + if (p == NULL && apol_mls_range_is_literal(range) != 1) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + goto cleanup; + } + + if ((sub_str = apol_mls_level_render(p, range->low)) == NULL) { + goto cleanup; + } + if (apol_str_append(&rt, &sz, sub_str)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + free(sub_str); + sub_str = NULL; + if (range->high == NULL) { + /* no high level set, so skip the rest of this render + * function */ + retval = rt; + goto cleanup; + } + if (p == NULL) { + // no policy, so assume that high level dominates low level + retv = APOL_MLS_DOM; + } else { + retv = apol_mls_level_compare(p, range->low, range->high); + if (retv < 0) { + goto cleanup; + } + } + /* if (high level != low level) */ + if ((retv == APOL_MLS_DOM || retv == APOL_MLS_DOMBY) && range->high != NULL) { + sub_str = apol_mls_level_render(p, range->high); + if (!sub_str) + goto cleanup; + if (apol_str_appendf(&rt, &sz, " - %s", sub_str)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + } + retval = rt; + cleanup: + if (retval != rt) { + free(rt); + } + free(sub_str); + return retval; +} + +int apol_mls_range_convert(const apol_policy_t * p, apol_mls_range_t * range) +{ + if (p == NULL || range == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + apol_mls_level_t *low = range->low; + apol_mls_level_t *high = range->high; + int retval; + if (low != NULL) { + retval = apol_mls_level_convert(p, low); + if (retval < 0) { + return retval; + } + } + if (high != NULL && high != low) { + retval = apol_mls_level_convert(p, high); + if (retval < 0) { + return retval; + } + } + return 0; +} + +int apol_mls_range_is_literal(const apol_mls_range_t * range) +{ + if (range == NULL) { + return -1; + } + int ret; + if ((ret = apol_mls_level_is_literal(range->low)) != 0) { + return ret; + } + if (range->high != NULL) { + ret = apol_mls_level_is_literal(range->high); + } + return ret; +} diff --git a/libapol/src/netcon-query.c b/libapol/src/netcon-query.c new file mode 100644 index 0000000..7faf0de --- /dev/null +++ b/libapol/src/netcon-query.c @@ -0,0 +1,592 @@ +/** + * @file + * + * Provides a way for setools to make queries about portcons, + * netifcons, and nodecons within a policy. The caller obtains a + * query object, fills in its parameters, and then runs the query; it + * obtains a vector of results. Searches are conjunctive -- all + * fields of the search query must match for a datum to be added to + * the results query. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" +#include <apol/render.h> + +#include <errno.h> +#include <string.h> + +struct apol_portcon_query +{ + int proto; + int low, high; + apol_context_t *context; + unsigned int flags; +}; + +struct apol_netifcon_query +{ + char *dev; + apol_context_t *if_context, *msg_context; + unsigned int if_flags, msg_flags; +}; + +struct apol_nodecon_query +{ + char proto, addr_proto, mask_proto; + uint32_t addr[4], mask[4]; + apol_context_t *context; + unsigned int flags; +}; + +/******************** portcon queries ********************/ + +int apol_portcon_get_by_query(const apol_policy_t * p, const apol_portcon_query_t * po, apol_vector_t ** v) +{ + qpol_iterator_t *iter; + int retval = -1, retval2; + *v = NULL; + if (qpol_policy_get_portcon_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_portcon_t *portcon; + if (qpol_iterator_get_item(iter, (void **)&portcon) < 0) { + goto cleanup; + } + if (po != NULL) { + uint16_t low, high; + uint8_t proto; + const qpol_context_t *context; + if (qpol_portcon_get_low_port(p->p, + portcon, &low) < 0 || + qpol_portcon_get_high_port(p->p, + portcon, &high) < 0 || + qpol_portcon_get_protocol(p->p, + portcon, &proto) < 0 || qpol_portcon_get_context(p->p, portcon, &context) < 0) + { + goto cleanup; + } + if ((po->low >= 0 && ((uint16_t) po->low) != low) || + (po->high >= 0 && ((uint16_t) po->high) != high) || (po->proto >= 0 && ((uint8_t) po->proto) != proto)) + { + continue; + } + retval2 = apol_compare_context(p, context, po->context, po->flags); + if (retval2 < 0) { + goto cleanup; + } else if (retval2 == 0) { + continue; + } + } + if (apol_vector_append(*v, portcon)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_portcon_query_t *apol_portcon_query_create(void) +{ + apol_portcon_query_t *po = calloc(1, sizeof(*po)); + if (po == NULL) { + return NULL; + } + po->proto = po->low = po->high = -1; + return po; +} + +void apol_portcon_query_destroy(apol_portcon_query_t ** po) +{ + if (*po != NULL) { + apol_context_destroy(&((*po)->context)); + free(*po); + *po = NULL; + } +} + +int apol_portcon_query_set_protocol(const apol_policy_t * p __attribute__ ((unused)), apol_portcon_query_t * po, int proto) +{ + po->proto = proto; + return 0; +} + +/** + * @deprecated Use apol_portcon_query_set_protocol() instead. + */ +int apol_portcon_query_set_proto(apol_policy_t * p, apol_portcon_query_t * po, int proto) +{ + return apol_portcon_query_set_protocol(p, po, proto); +} +int apol_portcon_query_set_proto(apol_policy_t * p, apol_portcon_query_t * po, int proto) __attribute__ ((deprecated)); + +int apol_portcon_query_set_low(const apol_policy_t * p __attribute__ ((unused)), apol_portcon_query_t * po, int low) +{ + po->low = low; + return 0; +} + +int apol_portcon_query_set_high(const apol_policy_t * p __attribute__ ((unused)), apol_portcon_query_t * po, int high) +{ + po->high = high; + return 0; +} + +int apol_portcon_query_set_context(const apol_policy_t * p __attribute__ ((unused)), + apol_portcon_query_t * po, apol_context_t * context, unsigned int range_match) +{ + if (po->context != NULL) { + apol_context_destroy(&po->context); + } + po->context = context; + po->flags = (po->flags & ~APOL_QUERY_FLAGS) | range_match; + return 0; +} + +char *apol_portcon_render(const apol_policy_t * p, const qpol_portcon_t * portcon) +{ + char *line = NULL, *retval = NULL; + char *buff = NULL; + const char *proto_str = NULL; + char *context_str = NULL; + const qpol_context_t *ctxt = NULL; + uint16_t low_port, high_port; + uint8_t proto; + + const size_t bufflen = 50; /* arbitrary size big enough to hold port no. */ + if (!portcon || !p) + goto cleanup; + + buff = (char *)calloc(bufflen + 1, sizeof(char)); + if (!buff) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + + if (qpol_portcon_get_protocol(p->p, portcon, &proto)) + goto cleanup; + + if ((proto_str = apol_protocol_to_str(proto)) == NULL) { + ERR(p, "%s", "Could not get protocol string."); + goto cleanup; + } + if (qpol_portcon_get_low_port(p->p, portcon, &low_port)) + goto cleanup; + if (qpol_portcon_get_high_port(p->p, portcon, &high_port)) + goto cleanup; + if (low_port == high_port) + snprintf(buff, bufflen, "%d", low_port); + else + snprintf(buff, bufflen, "%d-%d", low_port, high_port); + + if (qpol_portcon_get_context(p->p, portcon, &ctxt)) + goto cleanup; + context_str = apol_qpol_context_render(p, ctxt); + if (!context_str) + goto cleanup; + + line = (char *)calloc(4 + strlen("portcon") + strlen(proto_str) + strlen(buff) + strlen(context_str), sizeof(char)); + if (!line) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + + sprintf(line, "portcon %s %s %s", proto_str, buff, context_str); + + retval = line; + cleanup: + free(buff); + free(context_str); + if (retval != line) { + free(line); + } + return retval; +} + +/******************** netifcon queries ********************/ + +int apol_netifcon_get_by_query(const apol_policy_t * p, const apol_netifcon_query_t * n, apol_vector_t ** v) +{ + qpol_iterator_t *iter; + int retval = -1, retval2; + *v = NULL; + if (qpol_policy_get_netifcon_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + const qpol_netifcon_t *netifcon; + if (qpol_iterator_get_item(iter, (void **)&netifcon) < 0) { + goto cleanup; + } + if (n != NULL) { + const char *name; + const qpol_context_t *ifcon, *msgcon; + if (qpol_netifcon_get_name(p->p, netifcon, &name) < 0 || + qpol_netifcon_get_if_con(p->p, netifcon, &ifcon) < 0 || + qpol_netifcon_get_msg_con(p->p, netifcon, &msgcon) < 0) { + goto cleanup; + } + retval2 = apol_compare(p, name, n->dev, 0, NULL); + if (retval2 < 0) { + goto cleanup; + } else if (retval2 == 0) { + continue; + } + retval2 = apol_compare_context(p, ifcon, n->if_context, n->if_flags); + if (retval2 < 0) { + goto cleanup; + } else if (retval2 == 0) { + continue; + } + retval2 = apol_compare_context(p, msgcon, n->msg_context, n->msg_flags); + if (retval2 < 0) { + goto cleanup; + } else if (retval2 == 0) { + continue; + } + } + if (apol_vector_append(*v, (void *)netifcon)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_netifcon_query_t *apol_netifcon_query_create(void) +{ + return calloc(1, sizeof(apol_netifcon_query_t)); +} + +void apol_netifcon_query_destroy(apol_netifcon_query_t ** n) +{ + if (*n != NULL) { + free((*n)->dev); + apol_context_destroy(&((*n)->if_context)); + apol_context_destroy(&((*n)->msg_context)); + free(*n); + *n = NULL; + } +} + +int apol_netifcon_query_set_device(const apol_policy_t * p, apol_netifcon_query_t * n, const char *dev) +{ + return apol_query_set(p, &n->dev, NULL, dev); +} + +int apol_netifcon_query_set_if_context(const apol_policy_t * p __attribute__ ((unused)), + apol_netifcon_query_t * n, apol_context_t * context, unsigned int range_match) +{ + if (n->if_context != NULL) { + apol_context_destroy(&n->if_context); + } + n->if_context = context; + n->if_flags = (n->if_flags & ~APOL_QUERY_FLAGS) | range_match; + return 0; +} + +int apol_netifcon_query_set_msg_context(const apol_policy_t * p __attribute__ ((unused)), + apol_netifcon_query_t * n, apol_context_t * context, unsigned int range_match) +{ + if (n->msg_context != NULL) { + apol_context_destroy(&n->msg_context); + } + n->msg_context = context; + n->msg_flags = (n->msg_flags & ~APOL_QUERY_FLAGS) | range_match; + return 0; +} + +char *apol_netifcon_render(const apol_policy_t * p, const qpol_netifcon_t * netifcon) +{ + char *line = NULL, *retval = NULL; + char *devcon_str = NULL; + char *pktcon_str = NULL; + const char *iface_str = NULL; + const qpol_context_t *ctxt = NULL; + + if (!netifcon || !p) + goto cleanup; + + if (qpol_netifcon_get_if_con(p->p, netifcon, &ctxt)) + goto cleanup; + devcon_str = apol_qpol_context_render(p, ctxt); + if (!devcon_str) + goto cleanup; + + if (qpol_netifcon_get_msg_con(p->p, netifcon, &ctxt)) + goto cleanup; + pktcon_str = apol_qpol_context_render(p, ctxt); + if (!pktcon_str) { + goto cleanup; + } + + if (qpol_netifcon_get_name(p->p, netifcon, &iface_str)) + return NULL; + line = (char *)calloc(4 + strlen(iface_str) + strlen(devcon_str) + strlen(pktcon_str) + strlen("netifcon"), sizeof(char)); + if (!line) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + sprintf(line, "netifcon %s %s %s", iface_str, devcon_str, pktcon_str); + + retval = line; + cleanup: + free(devcon_str); + free(pktcon_str); + return retval; +} + +/******************** nodecon queries ********************/ + +int apol_nodecon_get_by_query(const apol_policy_t * p, const apol_nodecon_query_t * n, apol_vector_t ** v) +{ + qpol_iterator_t *iter; + int retval = -1, retval2; + qpol_nodecon_t *nodecon = NULL; + *v = NULL; + if (qpol_policy_get_nodecon_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(free)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&nodecon) < 0) { + goto cleanup; + } + if (n != NULL) { + unsigned char proto, proto_a, proto_m; + uint32_t *addr, *mask; + const qpol_context_t *con; + if (qpol_nodecon_get_protocol(p->p, nodecon, &proto) < 0 || + qpol_nodecon_get_addr(p->p, nodecon, &addr, &proto_a) < 0 || + qpol_nodecon_get_mask(p->p, nodecon, &mask, &proto_m) < 0 || + qpol_nodecon_get_context(p->p, nodecon, &con) < 0) { + goto cleanup; + } + if (n->proto >= 0 && n->proto != proto) { + free(nodecon); + continue; + } + if (n->addr_proto >= 0 && + (n->addr_proto != proto_a || + (proto_a == QPOL_IPV4 && memcmp(n->addr, addr, 1 * sizeof(uint32_t)) != 0) || + (proto_a == QPOL_IPV6 && memcmp(n->addr, addr, 4 * sizeof(uint32_t)) != 0))) { + free(nodecon); + continue; + } + if (n->mask_proto >= 0 && + (n->mask_proto != proto_m || + (proto_m == QPOL_IPV4 && memcmp(n->mask, mask, 1 * sizeof(uint32_t)) != 0) || + (proto_m == QPOL_IPV6 && memcmp(n->mask, mask, 4 * sizeof(uint32_t)) != 0))) { + free(nodecon); + continue; + } + retval2 = apol_compare_context(p, con, n->context, n->flags); + if (retval2 < 0) { + goto cleanup; + } else if (retval2 == 0) { + free(nodecon); + continue; + } + } + if (apol_vector_append(*v, nodecon)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + free(nodecon); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_nodecon_query_t *apol_nodecon_query_create(void) +{ + apol_nodecon_query_t *n = calloc(1, sizeof(apol_nodecon_query_t)); + if (n != NULL) { + n->proto = n->addr_proto = n->mask_proto = -1; + } + return n; +} + +void apol_nodecon_query_destroy(apol_nodecon_query_t ** n) +{ + if (*n != NULL) { + apol_context_destroy(&((*n)->context)); + free(*n); + *n = NULL; + } +} + +int apol_nodecon_query_set_protocol(const apol_policy_t * p, apol_nodecon_query_t * n, int proto) +{ + if (proto == QPOL_IPV4 || proto == QPOL_IPV6) { + n->proto = (char)proto; + } else if (proto < 0) { + n->proto = -1; + } else { + ERR(p, "Invalid protocol value %d.", proto); + return -1; + } + return 0; +} + +/** + * @deprecated Use apol_nodecon_query_set_protocol() instead. + */ +int apol_nodecon_query_set_proto(apol_policy_t * p, apol_nodecon_query_t * n, int proto) +{ + return apol_nodecon_query_set_protocol(p, n, proto); +} +int apol_nodecon_query_set_proto(apol_policy_t * p, apol_nodecon_query_t * n, int proto) __attribute__ ((deprecated)); + +int apol_nodecon_query_set_addr(const apol_policy_t * p, apol_nodecon_query_t * n, uint32_t * addr, int proto) +{ + if (addr == NULL) { + n->addr_proto = -1; + } else { + if (proto == QPOL_IPV4) { + memcpy(n->addr, addr, 1 * sizeof(uint32_t)); + } else if (proto == QPOL_IPV6) { + memcpy(n->addr, addr, 4 * sizeof(uint32_t)); + } else { + ERR(p, "Invalid protocol value %d.", proto); + return -1; + } + n->addr_proto = (char)proto; + } + return 0; +} + +int apol_nodecon_query_set_mask(const apol_policy_t * p, apol_nodecon_query_t * n, uint32_t * mask, int proto) +{ + if (mask == NULL) { + n->mask_proto = -1; + } else { + if (proto == QPOL_IPV4) { + memcpy(n->mask, mask, 1 * sizeof(uint32_t)); + } else if (proto == QPOL_IPV6) { + memcpy(n->mask, mask, 4 * sizeof(uint32_t)); + } else { + ERR(p, "Invalid protocol value %d.", proto); + return -1; + } + n->mask_proto = (char)proto; + } + return 0; +} + +int apol_nodecon_query_set_context(const apol_policy_t * p __attribute__ ((unused)), + apol_nodecon_query_t * n, apol_context_t * context, unsigned int range_match) +{ + if (n->context != NULL) { + apol_context_destroy(&n->context); + } + n->context = context; + n->flags = (n->flags & ~APOL_QUERY_FLAGS) | range_match; + return 0; +} + +char *apol_nodecon_render(const apol_policy_t * p, const qpol_nodecon_t * nodecon) +{ + char *line = NULL, *retval = NULL; + char *context_str = NULL; + char *addr_str = NULL; + char *mask_str = NULL; + const qpol_context_t *ctxt = NULL; + unsigned char protocol, addr_proto, mask_proto; + uint32_t *addr = NULL, *mask = NULL; + + if (!nodecon || !p) + goto cleanup; + + if (qpol_nodecon_get_protocol(p->p, nodecon, &protocol)) + goto cleanup; + if (qpol_nodecon_get_addr(p->p, nodecon, &addr, &addr_proto)) + goto cleanup; + if (qpol_nodecon_get_mask(p->p, nodecon, &mask, &mask_proto)) + goto cleanup; + switch (protocol) { + case QPOL_IPV4: + if ((addr_str = apol_ipv4_addr_render(p, addr)) == NULL || (mask_str = apol_ipv4_addr_render(p, mask)) == NULL) { + goto cleanup; + } + break; + case QPOL_IPV6: + if ((addr_str = apol_ipv6_addr_render(p, addr)) == NULL || (mask_str = apol_ipv6_addr_render(p, mask)) == NULL) { + goto cleanup; + } + break; + default: + break; + } + + if (qpol_nodecon_get_context(p->p, nodecon, &ctxt)) + goto cleanup; + context_str = apol_qpol_context_render(p, ctxt); + if (!context_str) + goto cleanup; + + line = (char *)calloc(4 + strlen("nodecon") + strlen(addr_str) + strlen(mask_str) + strlen(context_str), sizeof(char)); + if (!line) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + + sprintf(line, "nodecon %s %s %s", addr_str, mask_str, context_str); + + retval = line; + cleanup: + free(addr_str); + free(mask_str); + free(context_str); + return retval; +} diff --git a/libapol/src/perm-map.c b/libapol/src/perm-map.c new file mode 100644 index 0000000..01c4c32 --- /dev/null +++ b/libapol/src/perm-map.c @@ -0,0 +1,697 @@ +/** + * @file + * + * Implementation of permission mapping routines. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" + +#include <apol/perm-map.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +/* use 8k line size */ +#define APOL_LINE_SZ 8192 + +struct apol_permmap +{ + unsigned char mapped; /* true if this class's permissions + * were mapped from a file, false if + * using default values */ + apol_vector_t *classes; /* list of apol_permmap_class_t */ +}; + +/* There is one apol_permmap_class per object class. */ +typedef struct apol_permmap_class +{ + unsigned char mapped; /* mask */ + /** pointer to within a qpol_policy_t that represents this class */ + const qpol_class_t *c; + /** vector of apol_permmap_perm, an element for each permission bit */ + apol_vector_t *perms; +} apol_permmap_class_t; + +/** + * Permission maps: For each object class we need to map all permisions + * to either read and/or write, or non similar as is done for the MLS stuff. + * This allows us to determine information flow. These mappings will be + * loadable so that users can re-map them as they see fit. + */ +typedef struct apol_permmap_perm +{ + /** name of permission */ + char *name; + /** one of APOL_PERMMAP_READ, etc. */ + unsigned char map; + /** the weight (importance) of this perm. (least) 1 - 10 (most) */ + int weight; +} apol_permmap_perm_t; + +/* some perms unmapped */ +#define APOL_PERMMAP_RET_UNMAPPED_PERM 0x01 +/* some objects unmapped */ +#define APOL_PERMMAP_RET_UNMAPPED_OBJ 0x02 +/* some perms from file unknown and ignored */ +#define APOL_PERMMAP_RET_UNKNOWN_PERM 0x04 +/* some object from file unknown and ignored */ +#define APOL_PERMMAP_RET_UNKNOWN_OBJ 0x08 +/* not enough classes/perms */ +#define APOL_PERMMAP_RET_NOT_ENOUGH 0x10 + +/** + * Deallocate all space used by an apol_permmap_perm_t, including the + * pointer itself. + * + * @param elem Pointer to free. If NULL then do nothing. + */ +static void permmap_perm_free(void *elem) +{ + if (elem != NULL) { + apol_permmap_perm_t *p = (apol_permmap_perm_t *) elem; + free(p->name); + free(p); + } +} + +/** + * Deallocate all space used by an apol_permmap_class_t, including the + * pointer itself. + * + * @param elem Pointer to free. If NULL then do nothing. + */ +static void permmap_class_free(void *elem) +{ + if (elem != NULL) { + apol_permmap_class_t *c = (apol_permmap_class_t *) elem; + apol_vector_destroy(&c->perms); + free(c); + } +} + +/** + * Allocate and return a new apol_permmap_perm_t. + * + * @param name Name of the permission. This function will duplicate + * the string. + * @param map Direction of information flow. This must be one of + * APOL_PERMMAP_UNMAPPED, APOL_PERMMAP_READ, etc. + * @param weight Weight of the permission. This must be an integer + * from APOL_PERMMAP_MIN_WEIGHT to APOL_PERMMAP_MAX_WEIGHT, inclusive. + * + * @return A newly allocated apol_permmap_perm_t, or NULL on out of + * memory. The caller is responsible for deallocating this pointer + * via apol_permmap_perm_free(). + */ +static apol_permmap_perm_t *apol_permmap_perm_create(const char *name, unsigned char map, int weight) +{ + apol_permmap_perm_t *pp; + if ((pp = calloc(1, sizeof(*pp))) == NULL) { + return NULL; + } + if ((pp->name = strdup(name)) == NULL) { + free(pp); + return NULL; + } + pp->map = map; + pp->weight = weight; + return pp; +} + +/** + * Allocate and return a new permission map from a policy, and + * allocates space for defined object classes. + * + * @param p Policy from which to create permission map. + * + * @return A newly allocated map, or NULL on error. The caller is + * responsible for deallocating this pointer via permmap_destroy(). + */ +static apol_permmap_t *apol_permmap_create_from_policy(const apol_policy_t * p) +{ + apol_permmap_t *t = NULL; + qpol_iterator_t *class_iter = NULL, *perm_iter = NULL, *common_iter = NULL; + size_t num_obj_classes; + int retval = -1; + + if (p == NULL) { + goto cleanup; + } + + if ((t = (apol_permmap_t *) calloc(1, sizeof(*t))) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + if (qpol_policy_get_class_iter(p->p, &class_iter) < 0 || qpol_iterator_get_size(class_iter, &num_obj_classes) < 0) { + goto cleanup; + } + t->mapped = 0; + if ((t->classes = apol_vector_create_with_capacity(num_obj_classes, permmap_class_free)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(class_iter); qpol_iterator_next(class_iter)) { + const qpol_class_t *c; + const qpol_common_t *common; + apol_permmap_class_t *pc = NULL; + apol_permmap_perm_t *pp = NULL; + size_t num_unique_perms, num_common_perms = 0; + char *name; + if (qpol_iterator_get_item(class_iter, (void **)&c) < 0 || + qpol_class_get_perm_iter(p->p, c, &perm_iter) < 0 || + qpol_iterator_get_size(perm_iter, &num_unique_perms) < 0 || qpol_class_get_common(p->p, c, &common) < 0) { + goto cleanup; + } + if (common != NULL && + (qpol_common_get_perm_iter(p->p, common, &common_iter) < 0 || + qpol_iterator_get_size(common_iter, &num_common_perms) < 0)) { + goto cleanup; + } + if ((pc = calloc(1, sizeof(*pc))) == NULL || apol_vector_append(t->classes, pc) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + permmap_class_free(pc); + goto cleanup; + } + pc->mapped = 0; + pc->c = c; + if ((pc->perms = apol_vector_create_with_capacity(num_unique_perms + num_common_perms, permmap_perm_free)) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + /* initialize with all the class's unique permissions + * from provided policy */ + for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) { + if (qpol_iterator_get_item(perm_iter, (void **)&name) < 0) { + goto cleanup; + } + if ((pp = apol_permmap_perm_create(name, 0, (char)APOL_PERMMAP_MIN_WEIGHT)) == NULL || + apol_vector_append(pc->perms, pp) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + permmap_perm_free(pp); + goto cleanup; + } + } + /* next initialize with common permissions */ + for (; common_iter != NULL && !qpol_iterator_end(common_iter); qpol_iterator_next(common_iter)) { + if (qpol_iterator_get_item(common_iter, (void **)&name) < 0) { + goto cleanup; + } + if ((pp = apol_permmap_perm_create(name, 0, (char)APOL_PERMMAP_MIN_WEIGHT)) == NULL || + apol_vector_append(pc->perms, pp) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + permmap_perm_free(pp); + goto cleanup; + } + } + qpol_iterator_destroy(&perm_iter); + qpol_iterator_destroy(&common_iter); + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&class_iter); + qpol_iterator_destroy(&perm_iter); + qpol_iterator_destroy(&common_iter); + if (retval < 0) { + permmap_destroy(&t); + } + return t; +} + +void permmap_destroy(apol_permmap_t ** p) +{ + if (p == NULL || *p == NULL) + return; + apol_vector_destroy(&(*p)->classes); + free(*p); + *p = NULL; +} + +/** + * Searches through the permission map within a policy, returning the + * record for a given object class. + * + * @param p Policy containing permission map. + * @param target Target class name. + * + * @return Pointer to the class within the permission map, or NULL if + * not found or on error. + */ +static apol_permmap_class_t *find_permmap_class(const apol_policy_t * p, const char *target) +{ + size_t i; + const qpol_class_t *target_class; + if (qpol_policy_get_class_by_name(p->p, target, &target_class) < 0) { + return NULL; + } + for (i = 0; i < apol_vector_get_size(p->pmap->classes); i++) { + apol_permmap_class_t *pc = apol_vector_get_element(p->pmap->classes, i); + if (pc->c == target_class) { + return pc; + } + } + return NULL; +} + +/** + * Searches through the permission map's class, returning the record + * for a given permission. + * + * @param p Policy to use, for error handling. + * @param pc Permission map class to search. + * @param target Target class name. + * + * @return Pointer to the permission record within the class, or NULL + * if not found or on error. + */ +static apol_permmap_perm_t *find_permmap_perm(const apol_policy_t * p + __attribute__ ((unused)), const apol_permmap_class_t * pc, const char *target) +{ + size_t i; + for (i = 0; i < apol_vector_get_size(pc->perms); i++) { + apol_permmap_perm_t *pp = apol_vector_get_element(pc->perms, i); + if (strcmp(pp->name, target) == 0) { + return pp; + } + } + return NULL; +} + +/** + * Given a character representation, return its numerical permission + * map type. + * + * @param p Policy containing error handler. + * @param perm_name Name of the permission. + * @param mapid Character representing map type. + * + * @return One of APOL_PERMMAP_READ, etc, or APOL_PERMMAP_UNMAPPED on error. + */ +static char convert_map_char(const apol_policy_t * p, const char *perm_name, char mapid) +{ + switch (mapid) { + case 'r': + case 'R': + return APOL_PERMMAP_READ; + case 'w': + case 'W': + return APOL_PERMMAP_WRITE; + case 'b': + case 'B': + return APOL_PERMMAP_BOTH; + case 'n': + case 'N': + return APOL_PERMMAP_NONE; + default: + ERR(p, "Invalid map character '%c' for permission %s; permission will be unmapped.", mapid, perm_name); + return APOL_PERMMAP_UNMAPPED; + } +} + +/** + * Goes through a policy's permission map to check that all classes + * had an entry within the recently read permission map file. + * + * @param p Policy containing permission map to check. + * + * @return 1 if all classes had entries, 0 if any did not. + */ +static int are_all_classes_mapped(const apol_policy_t * p) +{ + size_t i; + for (i = 0; i < apol_vector_get_size(p->pmap->classes); i++) { + apol_permmap_class_t *pc = apol_vector_get_element(p->pmap->classes, i); + if (pc->mapped == 0) { + const char *class_name; + if (qpol_class_get_name(p->p, pc->c, &class_name) < 0) { + return 0; + } + WARN(p, "Some permissions were unmapped for class %s.", class_name); + return 0; + } + } + return 1; +} + +/** + * Goes through a class's permissions to check that had an entry + * within the recently read permission map file. + * + * @param p Policy containing permission map to check. + * @param pc Class to check. + * + * @return 1 if all permissions had entries, 0 if any did not. + */ +static int are_all_perms_mapped(const apol_policy_t * p, const apol_permmap_class_t * pc) +{ + size_t i; + for (i = 0; i < apol_vector_get_size(pc->perms); i++) { + apol_permmap_perm_t *pp = apol_vector_get_element(pc->perms, i); + if (pp->map == 0) { + const char *class_name; + if (qpol_class_get_name(p->p, pc->c, &class_name) < 0) { + return 0; + } + WARN(p, "Permission %s was unmapped for class %s.", pp->name, class_name); + return 0; + } + } + return 1; +} + +/** + * Parse the individual permission definitions for a given class. If + * pc is not NULL then store them into a apol_permmap_class_t, + * otherwise discard the read values. If a permission was read, but + * does not have an entry within pc, then generate a warning and + * continue. Finally, check that all permissions for the given class + * have been mapped; if any have not then generate a warning. + * + * @param p Policy containing permission map. + * @param fp File pointer that contains permission map data. + * @param num_perms Number of permissions expected to be read. + * @param pc Destination to store data; if NULL then do not store data. + * + * @return 0 on success, > 0 on success but with warnings, < 0 on + * error. + */ +static int parse_permmap_class(apol_policy_t * p, FILE * fp, size_t num_perms, apol_permmap_class_t * pc) +{ + char line[APOL_LINE_SZ], perm_name[APOL_LINE_SZ], *line_ptr = NULL; + size_t perms_read = 0; + int retval = 0; + + while (fgets(line, sizeof(line), fp) != NULL && perms_read < num_perms) { + char mapid; + int perm_weight, new_weight; + apol_permmap_perm_t *pp; + + line_ptr = line; + apol_str_trim(line_ptr); + if (line_ptr[0] == '#' || apol_str_is_only_white_space(line_ptr)) + continue; + perms_read++; + if (sscanf(line_ptr, "%s %c %d", perm_name, &mapid, &perm_weight) != 3) { + /* This may be a perm map file w/o perm weighting. */ + if (sscanf(line_ptr, "%s %c", perm_name, &mapid) != 2) { + ERR(p, "Permission map has an invalid line: \"%s\"", line_ptr); + return -1; + } + perm_weight = APOL_PERMMAP_MAX_WEIGHT; + } + if (strcmp(perm_name, "class") == 0) { + ERR(p, "There were supposed to be %zu permissions, but only %zu were found.", num_perms, perms_read); + return -1; + } + new_weight = perm_weight; + if (perm_weight > APOL_PERMMAP_MAX_WEIGHT) { + new_weight = APOL_PERMMAP_MAX_WEIGHT; + } else if (perm_weight < APOL_PERMMAP_MIN_WEIGHT) { + new_weight = APOL_PERMMAP_MIN_WEIGHT; + } + if (new_weight != perm_weight) { + WARN(p, "Permission %s's weight %d is invalid. Setting it to %d instead.", perm_name, perm_weight, + new_weight); + perm_weight = new_weight; + } + if (pc != NULL) { + if ((pp = find_permmap_perm(p, pc, perm_name)) == NULL) { + WARN(p, + "Permission %s was defined in the permission map file but not within the policy. It will be ignored.", + perm_name); + retval |= APOL_PERMMAP_RET_UNKNOWN_PERM; + } else { + pp->weight = perm_weight; + pp->map = convert_map_char(p, perm_name, mapid); + } + } + } + if (perms_read != num_perms) { + WARN(p, "There were supposed to be %zu permissions, but only %zu were found.", num_perms, perms_read); + retval |= APOL_PERMMAP_RET_NOT_ENOUGH; + } + if (pc != NULL && !are_all_perms_mapped(p, pc)) { + retval |= APOL_PERMMAP_RET_UNMAPPED_PERM; + } + return retval; +} + +/** + * Parse the permission map found within a file pointer, storing the + * information into the map within a policy. If there is a non-fatal + * error while loading (e.g., file declared an object class that does + * not exist within the policy) then generate a warning string and + * send it to the error handler stored within the policy. + * + * @param p Policy containing a newly allocated permission map. + * @param fp File pointer that contains permission map data. + * + * @return 0 on success, > 0 on success but with warnings, < 0 on + * error. + */ +static int parse_permmap(apol_policy_t * p, FILE * fp) +{ + char line[APOL_LINE_SZ], class_name[APOL_LINE_SZ], *line_ptr = NULL; + size_t num_classes = 0, num_perms = 0; + size_t i; + int retval = 0; + + /* first read number of classes */ + while (fgets(line, sizeof(line), fp) != NULL) { + line_ptr = line;; + apol_str_trim(line_ptr); + if (line_ptr[0] != '#' && (sscanf(line_ptr, "%zu", &num_classes) == 1)) { + break; + } + } + if (num_classes == 0) { + ERR(p, "%s", "No object classes were defined in the permission map file."); + return -1; + } + + /* next read each class */ + for (i = 0; i < num_classes; i++) { + apol_permmap_class_t *pc; + int found_class_decl = 0, rt; + while (fgets(line, APOL_LINE_SZ, fp) != NULL) { + line_ptr = line; + apol_str_trim(line_ptr); + if (line_ptr[0] != '#' && (sscanf(line_ptr, "%*s %s %zu", class_name, &num_perms) == 2)) { + found_class_decl = 1; + break; + } + } + if (!found_class_decl) { + WARN(p, "Permission map file was supposed to have %zu classes, but only %zu were found.", num_classes, i); + return APOL_PERMMAP_RET_NOT_ENOUGH; + } + if ((pc = find_permmap_class(p, class_name)) == NULL) { + WARN(p, + "Object class %s was defined in the permission map file but not within the policy. It will be ignored.", + class_name); + /* skip to next record */ + parse_permmap_class(p, fp, num_perms, NULL); + retval |= APOL_PERMMAP_RET_UNKNOWN_OBJ; + } else { + if ((rt = parse_permmap_class(p, fp, num_perms, pc)) < 0) { + return -1; + } + pc->mapped = 1; + retval |= rt; + } + } + return retval; +} + +int apol_policy_open_permmap(apol_policy_t * p, const char *filename) +{ + FILE *outfile = NULL; + int retval = -1, rt = 0; + + if (p == NULL || filename == NULL) { + goto cleanup; + } + permmap_destroy(&p->pmap); + if ((p->pmap = apol_permmap_create_from_policy(p)) == NULL) { + goto cleanup; + } + + if ((outfile = fopen(filename, "r")) == NULL) { + ERR(p, "Could not open permission map %s for reading: %s", filename, strerror(errno)); + goto cleanup; + } + + if ((rt = parse_permmap(p, outfile)) < 0) { + goto cleanup; + } + + /* check that all classes have been mapped */ + if (rt == 0 && !are_all_classes_mapped(p)) { + rt = APOL_PERMMAP_RET_UNMAPPED_OBJ; + } + p->pmap->mapped = 1; + + retval = rt; + cleanup: + if (outfile != NULL) { + fclose(outfile); + } + return retval; +} + +int apol_permmap_load(apol_policy_t * p, const char *filename) +{ + return apol_policy_open_permmap(p, filename); +} + +int apol_policy_save_permmap(const apol_policy_t * p, const char *filename) +{ + time_t ltime; + size_t i, j; + FILE *outfile = NULL; + int retval = -1; + + if (p == NULL || p->pmap == NULL || filename == NULL) + goto cleanup; + + if ((outfile = fopen(filename, "w")) == NULL) { + ERR(p, "Could not open permission map %s for writing: %s", filename, strerror(errno)); + goto cleanup; + } + + if (time(<ime) == (time_t) - 1) { + ERR(p, "Could not get time: %s", strerror(errno)); + goto cleanup; + } + if (fprintf(outfile, "# Auto-generated by apol on %s\n", ctime(<ime)) < 0 || + fprintf(outfile, "#\n# permission map file\n\n\n") < 0 || + fprintf(outfile, "Number of classes (mapped?: %s):\n", (p->pmap->mapped ? "yes" : "no")) < 0 || + fprintf(outfile, "%zu\n", apol_vector_get_size(p->pmap->classes)) < 0) { + ERR(p, "Write error: %s", strerror(errno)); + goto cleanup; + } + + for (i = 0; i < apol_vector_get_size(p->pmap->classes); i++) { + apol_permmap_class_t *pc = apol_vector_get_element(p->pmap->classes, i); + const char *class_name; + if (qpol_class_get_name(p->p, pc->c, &class_name) < 0) { + goto cleanup; + } + if (fprintf(outfile, "\nclass %s %zu\n", class_name, apol_vector_get_size(pc->perms)) < 0) { + ERR(p, "Write error: %s", strerror(errno)); + goto cleanup; + } + + for (j = 0; j < apol_vector_get_size(pc->perms); j++) { + apol_permmap_perm_t *pp = apol_vector_get_element(pc->perms, j); + char *s; + if (fprintf(outfile, "%s%18s ", pp->map & APOL_PERMMAP_UNMAPPED ? "#" : "", pp->name) < 0) { + ERR(p, "Write error: %s", strerror(errno)); + goto cleanup; + } + switch (pp->map) { + case APOL_PERMMAP_READ: + s = "r"; + break; + case APOL_PERMMAP_WRITE: + s = "w"; + break; + case APOL_PERMMAP_BOTH: + s = "b"; + break; + case APOL_PERMMAP_NONE: + s = "n"; + break; + case APOL_PERMMAP_UNMAPPED: + s = "u"; + break; + default: + s = "?"; + } + if (fprintf(outfile, "%s %10d\n", s, pp->weight) < 0) { + ERR(p, "Write error: %s", strerror(errno)); + goto cleanup; + } + } + } + + retval = 0; + cleanup: + if (outfile != NULL) { + fclose(outfile); + } + return retval; +} + +int apol_permmap_save(apol_policy_t * p, const char *filename) +{ + return apol_policy_save_permmap(p, filename); +} + +int apol_policy_get_permmap(const apol_policy_t * p, const char *class_name, const char *perm_name, int *map, int *weight) +{ + apol_permmap_class_t *pc; + apol_permmap_perm_t *pp; + if (p == NULL || p->pmap == NULL) { + return -1; + } + if ((pc = find_permmap_class(p, class_name)) == NULL || (pp = find_permmap_perm(p, pc, perm_name)) == NULL) { + ERR(p, "Could not find permission %s in class %s.", perm_name, class_name); + return -1; + } + *map = pp->map; + *weight = pp->weight; + return 0; +} + +int apol_permmap_get(apol_policy_t * p, const char *class_name, const char *perm_name, int *map, int *weight) +{ + return apol_policy_get_permmap(p, class_name, perm_name, map, weight); +} + +int apol_policy_set_permmap(apol_policy_t * p, const char *class_name, const char *perm_name, int map, int weight) +{ + apol_permmap_class_t *pc; + apol_permmap_perm_t *pp; + if (p == NULL || p->pmap == NULL) { + return -1; + } + if ((pc = find_permmap_class(p, class_name)) == NULL || (pp = find_permmap_perm(p, pc, perm_name)) == NULL) { + ERR(p, "Could not find permission %s in class %s.", perm_name, class_name); + return -1; + } + pp->map = map; + if (weight > APOL_PERMMAP_MAX_WEIGHT) { + weight = APOL_PERMMAP_MAX_WEIGHT; + } else if (weight < APOL_PERMMAP_MIN_WEIGHT) { + weight = APOL_PERMMAP_MIN_WEIGHT; + } + pp->weight = weight; + return 0; +} + +int apol_permmap_set(apol_policy_t * p, const char *class_name, const char *perm_name, int map, int weight) +{ + return apol_policy_set_permmap(p, class_name, perm_name, map, weight); +} diff --git a/libapol/src/permissive-query.c b/libapol/src/permissive-query.c new file mode 100644 index 0000000..1279ee7 --- /dev/null +++ b/libapol/src/permissive-query.c @@ -0,0 +1,107 @@ +/** + * @file + * + * Provides a way for setools to make queries about permissive types + * within a policy. The caller obtains a query object, + * fills in its parameters, and then runs the query; it obtains a + * vector of results. Searches are conjunctive -- all fields of the + * search query must match for a datum to be added to the results + * query. + * + * @author Steve Lawrence slawrence@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" + +#include <errno.h> + +struct apol_permissive_query +{ + char *permissive_name; + unsigned int flags; + regex_t *regex; +}; + +int apol_permissive_get_by_query(const apol_policy_t * p, apol_permissive_query_t * q, apol_vector_t ** v) +{ + qpol_iterator_t *iter; + int retval = -1; + *v = NULL; + if (qpol_policy_get_permissive_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + const qpol_permissive_t *permissive; + if (qpol_iterator_get_item(iter, (void **)&permissive) < 0) { + goto cleanup; + } + if (q != NULL) { + int compval = apol_compare_permissive(p, + permissive, q->permissive_name, + q->flags, &(q->regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + } + if (apol_vector_append(*v, (void *)permissive)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_permissive_query_t *apol_permissive_query_create(void) +{ + return calloc(1, sizeof(apol_permissive_query_t)); +} + +void apol_permissive_query_destroy(apol_permissive_query_t ** q) +{ + if (*q != NULL) { + free((*q)->permissive_name); + apol_regex_destroy(&(*q)->regex); + free(*q); + *q = NULL; + } +} + +int apol_permissive_query_set_name(const apol_policy_t * p, apol_permissive_query_t * q, const char *name) +{ + return apol_query_set(p, &q->permissive_name, &q->regex, name); +} + +int apol_permissive_query_set_regex(const apol_policy_t * p, apol_permissive_query_t * q, int is_regex) +{ + return apol_query_set_regex(p, &q->flags, is_regex); +} + diff --git a/libapol/src/polcap-query.c b/libapol/src/polcap-query.c new file mode 100644 index 0000000..3491485 --- /dev/null +++ b/libapol/src/polcap-query.c @@ -0,0 +1,107 @@ +/** + * @file + * + * Provides a way for setools to make queries about policy capabilities + * within a policy. The caller obtains a query object, + * fills in its parameters, and then runs the query; it obtains a + * vector of results. Searches are conjunctive -- all fields of the + * search query must match for a datum to be added to the results + * query. + * + * @author Steve Lawrence slawrence@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" + +#include <errno.h> + +struct apol_polcap_query +{ + char *polcap_name; + unsigned int flags; + regex_t *regex; +}; + +int apol_polcap_get_by_query(const apol_policy_t * p, apol_polcap_query_t * q, apol_vector_t ** v) +{ + qpol_iterator_t *iter; + int retval = -1; + *v = NULL; + if (qpol_policy_get_polcap_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + const qpol_polcap_t *polcap; + if (qpol_iterator_get_item(iter, (void **)&polcap) < 0) { + goto cleanup; + } + if (q != NULL) { + int compval = apol_compare_polcap(p, + polcap, q->polcap_name, + q->flags, &(q->regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + } + if (apol_vector_append(*v, (void *)polcap)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_polcap_query_t *apol_polcap_query_create(void) +{ + return calloc(1, sizeof(apol_polcap_query_t)); +} + +void apol_polcap_query_destroy(apol_polcap_query_t ** q) +{ + if (*q != NULL) { + free((*q)->polcap_name); + apol_regex_destroy(&(*q)->regex); + free(*q); + *q = NULL; + } +} + +int apol_polcap_query_set_name(const apol_policy_t * p, apol_polcap_query_t * q, const char *name) +{ + return apol_query_set(p, &q->polcap_name, &q->regex, name); +} + +int apol_polcap_query_set_regex(const apol_policy_t * p, apol_polcap_query_t * q, int is_regex) +{ + return apol_query_set_regex(p, &q->flags, is_regex); +} + diff --git a/libapol/src/policy-path.c b/libapol/src/policy-path.c new file mode 100644 index 0000000..3633ebd --- /dev/null +++ b/libapol/src/policy-path.c @@ -0,0 +1,409 @@ +/** + * @file + * + * Implementation of policy path object. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <apol/policy-path.h> +#include <apol/util.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +static const char *POLICY_PATH_MAGIC = "policy_list"; +static const int POLICY_PATH_MAX_VERSION = 1; + +struct apol_policy_path +{ + apol_policy_path_type_e path_type; + char *base; + apol_vector_t *modules; +}; + +apol_policy_path_t *apol_policy_path_create(apol_policy_path_type_e path_type, const char *path, const apol_vector_t * modules) +{ + apol_policy_path_t *p = NULL; + + if (path == NULL) { + errno = EINVAL; + return NULL; + } + if ((p = calloc(1, sizeof(*p))) == NULL) { + return NULL; + } + p->path_type = path_type; + if ((p->base = strdup(path)) == NULL) { + apol_policy_path_destroy(&p); + return NULL; + } + if (p->path_type == APOL_POLICY_PATH_TYPE_MODULAR) { + if (modules == NULL) { + p->modules = apol_vector_create(free); + } else { + p->modules = apol_vector_create_from_vector(modules, apol_str_strdup, NULL, free); + } + if (p->modules == NULL) { + apol_policy_path_destroy(&p); + return NULL; + } + apol_vector_sort_uniquify(p->modules, apol_str_strcmp, NULL); + } + return p; +} + +apol_policy_path_t *apol_policy_path_create_from_policy_path(const apol_policy_path_t * path) +{ + apol_policy_path_t *p; + if (path == NULL) { + errno = EINVAL; + return NULL; + } + p = apol_policy_path_create(path->path_type, path->base, path->modules); + return p; +} + +apol_policy_path_t *apol_policy_path_create_from_file(const char *filename) +{ + FILE *f = NULL; + apol_policy_path_t *path = NULL; + apol_policy_path_type_e path_type; + char *line = NULL, *s; + apol_vector_t *header_tokens = NULL; + size_t len; + int read_base = 0, retval = -1, error = 0; + + if (filename == NULL) { + error = EINVAL; + goto cleanup; + } + if ((f = fopen(filename, "r")) == NULL) { + error = errno; + goto cleanup; + } + + if (getline(&line, &len, f) < 0) { + error = EIO; + goto cleanup; + } + apol_str_trim(line); + if (strncmp(line, POLICY_PATH_MAGIC, strlen(POLICY_PATH_MAGIC)) != 0) { + error = EIO; + goto cleanup; + } + + apol_str_trim(line); + if ((header_tokens = apol_str_split(line, " ")) == NULL) { + error = errno; + goto cleanup; + } + if (apol_vector_get_size(header_tokens) < 3) { + error = EIO; + goto cleanup; + } + s = apol_vector_get_element(header_tokens, 1); + if (atoi(s) == 0 || atoi(s) > POLICY_PATH_MAX_VERSION) { + error = ENOTSUP; + goto cleanup; + } + s = apol_vector_get_element(header_tokens, 2); + if (strcmp(s, "monolithic") == 0) { + path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; + } else if (strcmp(s, "modular") == 0) { + path_type = APOL_POLICY_PATH_TYPE_MODULAR; + } else { + error = EIO; + goto cleanup; + } + + while (getline(&line, &len, f) >= 0) { + apol_str_trim(line); + if (line[0] == '#') { + continue; + } + if (!read_base) { + /* trying to parse a base policy / monolithic policy line */ + if ((path = apol_policy_path_create(path_type, line, NULL)) == NULL) { + error = errno; + goto cleanup; + } + read_base = 1; + } else { + /* trying to parse a module line */ + if (path_type == APOL_POLICY_PATH_TYPE_MONOLITHIC) { + error = EIO; + goto cleanup; + } else { + if ((s = strdup(line)) == NULL || apol_vector_append(path->modules, s) < 0) { + error = errno; + free(s); + goto cleanup; + } + } + } + } + if (read_base == 0) { + error = EIO; + goto cleanup; + } + retval = 0; + cleanup: + if (f != NULL) { + fclose(f); + } + free(line); + apol_vector_destroy(&header_tokens); + if (retval != 0) { + apol_policy_path_destroy(&path); + errno = error; + } + return path; +} + +apol_policy_path_t *apol_policy_path_create_from_string(const char *path_string) +{ + apol_policy_path_t *p = NULL; + apol_vector_t *tokens = NULL; + apol_policy_path_type_e path_type; + char *s; + size_t i; + if (path_string == NULL) { + errno = EINVAL; + return NULL; + } + if ((tokens = apol_str_split(path_string, ":")) == NULL) { + return NULL; + } + + /* first token identifies the path type */ + if (apol_vector_get_size(tokens) < 2) { + apol_vector_destroy(&tokens); + return NULL; + } + s = apol_vector_get_element(tokens, 0); + if (strcmp(s, "monolithic") == 0) { + path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; + } else if (strcmp(s, "modular") == 0) { + path_type = APOL_POLICY_PATH_TYPE_MODULAR; + } else { + apol_vector_destroy(&tokens); + errno = EINVAL; + return NULL; + } + + /* second token identifies gives base path */ + s = apol_vector_get_element(tokens, 1); + if ((p = apol_policy_path_create(path_type, s, NULL)) == NULL) { + apol_vector_destroy(&tokens); + return NULL; + } + + if (path_type == APOL_POLICY_PATH_TYPE_MODULAR) { + /* remainder are module paths */ + for (i = 2; i < apol_vector_get_size(tokens); i++) { + s = apol_vector_get_element(tokens, i); + if ((s = strdup(s)) == NULL || apol_vector_append(p->modules, s) < 0) { + free(s); + apol_vector_destroy(&tokens); + apol_policy_path_destroy(&p); + return NULL; + } + } + apol_vector_sort_uniquify(p->modules, apol_str_strcmp, NULL); + } + return p; +} + +void apol_policy_path_destroy(apol_policy_path_t ** path) +{ + if (path != NULL && *path != NULL) { + free((*path)->base); + apol_vector_destroy(&(*path)->modules); + free(*path); + *path = NULL; + } +} + +int apol_policy_path_compare(const apol_policy_path_t * a, const apol_policy_path_t * b) +{ + int cmp; + if (a == NULL || b == NULL) { + errno = EINVAL; + return 0; + } + if ((cmp = a->path_type - b->path_type) != 0) { + return cmp; + } + if ((cmp = strcmp(a->base, b->base)) != 0) { + return cmp; + } + if (a->path_type == APOL_POLICY_PATH_TYPE_MODULAR) { + /* only compare module vector if that field is relevant */ + size_t i; + cmp = apol_vector_compare(a->modules, b->modules, apol_str_strcmp, NULL, &i); + if (cmp != 0) { + return cmp; + } + } + return 0; +} + +apol_policy_path_type_e apol_policy_path_get_type(const apol_policy_path_t * path) +{ + if (path == NULL) { + errno = EINVAL; + return APOL_POLICY_PATH_TYPE_MONOLITHIC; + } + return path->path_type; +} + +const char *apol_policy_path_get_primary(const apol_policy_path_t * path) +{ + if (path == NULL) { + errno = EINVAL; + return NULL; + } + return path->base; +} + +const apol_vector_t *apol_policy_path_get_modules(const apol_policy_path_t * path) +{ + if (path == NULL || path->path_type != APOL_POLICY_PATH_TYPE_MODULAR) { + errno = EINVAL; + return NULL; + } + return path->modules; +} + +int apol_policy_path_to_file(const apol_policy_path_t * path, const char *filename) +{ + FILE *f = NULL; + char *path_type; + size_t i; + int retval = -1, error = 0; + if (path == NULL || filename == NULL) { + errno = EINVAL; + goto cleanup; + } + if ((f = fopen(filename, "w")) == NULL) { + error = errno; + goto cleanup; + } + if (path->path_type == APOL_POLICY_PATH_TYPE_MODULAR) { + path_type = "modular"; + } else { + path_type = "monolithic"; + } + if (fprintf(f, "%s %d %s\n", POLICY_PATH_MAGIC, POLICY_PATH_MAX_VERSION, path_type) < 0) { + error = errno; + goto cleanup; + } + if (fprintf(f, "%s\n", path->base) < 0) { + error = errno; + goto cleanup; + } + if (path->path_type == APOL_POLICY_PATH_TYPE_MODULAR) { + for (i = 0; i < apol_vector_get_size(path->modules); i++) { + char *m = apol_vector_get_element(path->modules, i); + if (fprintf(f, "%s\n", m) < 0) { + error = errno; + goto cleanup; + } + } + } + + retval = 0; + cleanup: + if (f != NULL) { + fclose(f); + } + if (retval != 0) { + error = errno; + } + return retval; +} + +char *apol_policy_path_to_string(const apol_policy_path_t * path) +{ + char *path_type; + char *s = NULL; + size_t len = 0, i; + if (path == NULL) { + errno = EINVAL; + return NULL; + } + if (path->path_type == APOL_POLICY_PATH_TYPE_MODULAR) { + path_type = "modular"; + } else { + path_type = "monolithic"; + } + if (apol_str_appendf(&s, &len, "%s:%s", path_type, path->base) < 0) { + return NULL; + } + if (path->path_type == APOL_POLICY_PATH_TYPE_MODULAR) { + for (i = 0; i < apol_vector_get_size(path->modules); i++) { + char *m = apol_vector_get_element(path->modules, i); + if (apol_str_appendf(&s, &len, ":%s", m) < 0) { + return NULL; + } + } + } + return s; +} + +int apol_file_is_policy_path_list(const char *filename) +{ + FILE *f = NULL; + char *line = NULL; + size_t len = 0; + int retval = -1, error = 0; + + if (filename == NULL) { + error = EINVAL; + goto cleanup; + } + if ((f = fopen(filename, "r")) == NULL) { + error = errno; + goto cleanup; + } + + if (getline(&line, &len, f) < 0) { + error = EIO; + goto cleanup; + } + apol_str_trim(line); + if (strncmp(line, POLICY_PATH_MAGIC, strlen(POLICY_PATH_MAGIC)) != 0) { + retval = 0; + goto cleanup; + } + retval = 1; + + cleanup: + if (f) + fclose(f); + free(line); + if (retval < 0) + errno = error; + return retval; +} diff --git a/libapol/src/policy-query-internal.h b/libapol/src/policy-query-internal.h new file mode 100644 index 0000000..657c815 --- /dev/null +++ b/libapol/src/policy-query-internal.h @@ -0,0 +1,511 @@ +/** + * @file + * + * Header for routines shared among libapol's queries and analyses. + * These routines are declared hidden within the library by way of the + * linking map. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_POLICY_QUERY_INTERNAL_H +#define APOL_POLICY_QUERY_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <config.h> + +#include <apol/policy.h> +#include <apol/policy-query.h> +#include <apol/util.h> +#include <apol/vector.h> + +#include <regex.h> +#include <stdlib.h> +#include <qpol/policy.h> + +/* forward declaration. the definition resides within perm-map.c */ + struct apol_permmap; + +/* forward declaration. the definition resides within domain-trans-analysis.c */ + typedef struct apol_domain_trans_table apol_domain_trans_table_t; + +/* declared in perm-map.c */ + typedef struct apol_permmap apol_permmap_t; + + struct apol_policy + { + qpol_policy_t *p; + apol_callback_fn_t msg_callback; + void *msg_callback_arg; + int policy_type; + /** permission mapping for this policy; mappings loaded as needed */ + struct apol_permmap *pmap; + /** for domain trans analysis; table built as needed */ + struct apol_domain_trans_table *domain_trans_table; + }; + +/** Every query allows the treatment of strings as regular expressions + * instead. Within the query structure are flags; if the first bit + * is set then use regex matching instead. */ +#define APOL_QUERY_REGEX 0x01 + +#define APOL_QUERY_ONLY_ENABLED 0x10 +#define APOL_QUERY_SOURCE_AS_ANY 0x20 +#define APOL_QUERY_SOURCE_INDIRECT 0x40 +#define APOL_QUERY_TARGET_INDIRECT 0x80 + +#define APOL_QUERY_SYMBOL_IS_BOTH (APOL_QUERY_SYMBOL_IS_TYPE|APOL_QUERY_SYMBOL_IS_ATTRIBUTE) +#define APOL_QUERY_SOURCE_TYPE 0x100 +#define APOL_QUERY_SOURCE_ATTRIBUTE 0x200 +#define APOL_QUERY_TARGET_TYPE 0x400 +#define APOL_QUERY_TARGET_ATTRIBUTE 0x800 + +#define APOL_QUERY_MATCH_ALL_PERMS 0x1000 + +/** + * Destroy a compiled regular expression, setting it to NULL + * afterwards. Does nothing if the reference is NULL. + * @param regex Regular expression to destroy. + */ + void apol_regex_destroy(regex_t ** regex); + +/** + * Sets a string field within a query, clearing its old contents and + * cached regex first. The search name will be duplicated. + * + * @param p Policy handler. + * @param search_name Reference to where to store duplicated name. + * @param regex Reference to cached regex; this will be cleared by the + * function. + * @param name New name to set, or NULL to just clear the field. + * + * @return 0 on success, < 0 on error. + */ + int apol_query_set(const apol_policy_t * p, char **query_name, regex_t ** regex, const char *name); + +/** + * Sets an arbitrary flag for a query structure. + * + * @param p Policy handler. + * @param flags Reference to a flag bitmap. + * @param is_flag If non-zero, set flag. Otherwise unset it. + * @param flag_value Flag value to set. + * + * @return Always returns 0. + */ + int apol_query_set_flag(const apol_policy_t * p, unsigned int *flags, const int is_flag, int flag_value); + +/** + * Sets the regular expression flag for a query structure. + * + * @param p Policy handler. + * @param flags Reference to the regular expression flag. + * @param is_regex If non-zero, set regex flag. Otherwise unset it. + * + * @return Always returns 0. + */ + int apol_query_set_regex(const apol_policy_t * p, unsigned int *flags, const int is_regex); + +/** + * Determines if a name matches a target symbol name. If flags has + * the APOL_QUERY_REGEX bit set, then (1) compile the regular + * expression if NULL, and (2) apply it to target. Otherwise do a + * string comparison between name and target. If name is NULL and/or + * empty then the comparison always succeeds regardless of flags and + * regex. + * + * @param p Policy handler. + * @param target Name of target symbol to compare. + * @param name Source target from which to compare. + * @param flags If APOL_QUERY_REGEX bit is set, treat name as a + * regular expression. + * @param regex If using regexp comparison, the compiled regular + * expression to use; the pointer will be allocated space if regexp is + * legal. If NULL, then compile the regexp pattern given by name and + * cache it here. + * + * @return 1 If comparison succeeds, 0 if not; < 0 on error. + */ + int apol_compare(const apol_policy_t * p, const char *target, const char *name, unsigned int flags, regex_t ** regex); + +/** + * Given an iterator of strings, checks if name matches any element + * within it. If there is a match, either literally or by regular + * expression, then return 1. If there are no matches then return 0. + * + * @param p Policy handler. + * @param iter Iterator of strings to match. + * @param name Source target from which to compare. + * @param flags If APOL_QUERY_REGEX bit is set, treat name as a + * regular expression. + * @param regex If using regexp comparison, the compiled regular + * expression to use; the pointer will be allocated space if regexp is + * legal. If NULL, then compile the regexp pattern given by name and + * cache it here. + * @param do_free If non-zero free the strings returned by the iterator. + * + * @return 1 If comparison succeeds, 0 if not; < 0 on error. + */ + int apol_compare_iter(const apol_policy_t * p, qpol_iterator_t * iter, const char *name, + unsigned int flags, regex_t ** regex, int do_free); + +/** + * Determines if a (partial) type query matches a qpol_type_t, + * either the type name or any of its aliases. + * + * @param p Policy within which to look up types. + * @param type Type datum to compare against. + * @param name Source target from which to compare. + * @param flags If APOL_QUERY_REGEX bit is set, treat name as a + * regular expression. + * @param regex If using regexp comparison, the compiled regular + * expression to use; the pointer will be allocated space if regexp is + * legal. If NULL, then compile the regexp pattern given by name and + * cache it here. + * + * @return 1 If comparison succeeds, 0 if not; < 0 on error. + */ + int apol_compare_type(const apol_policy_t * p, const qpol_type_t * type, const char *name, unsigned int flags, + regex_t ** type_regex); + +/** + * Determines if a (partial) permissive query matches a qpol_permissive_t, + * by name. + * + * @param p Policy within which to look up types. + * @param type Permissive datum to compare against. + * @param name Source target from which to compare. + * @param flags If APOL_QUERY_REGEX bit is set, treat name as a + * regular expression. + * @param regex If using regexp comparison, the compiled regular + * expression to use; the pointer will be allocated space if regexp is + * legal. If NULL, then compile the regexp pattern given by name and + * cache it here. + * + * @return 1 If comparison succeeds, 0 if not; < 0 on error. + */ + int apol_compare_permissive(const apol_policy_t * p, const qpol_permissive_t * permissive, const char *name, unsigned int flags, + regex_t ** type_regex); + +/** + * Determines if a (partial) polcap query matches a qpol_polcap_t, + * by name. + * + * @param p Policy within which to look up types. + * @param type Polcap datum to compare against. + * @param name Source target from which to compare. + * @param flags If APOL_QUERY_REGEX bit is set, treat name as a + * regular expression. + * @param regex If using regexp comparison, the compiled regular + * expression to use; the pointer will be allocated space if regexp is + * legal. If NULL, then compile the regexp pattern given by name and + * cache it here. + * + * @return 1 If comparison succeeds, 0 if not; < 0 on error. + */ + int apol_compare_polcap(const apol_policy_t * p, const qpol_polcap_t * polcap, const char *name, unsigned int flags, + regex_t ** type_regex); + +/** + * Determines if a boolean is used within a particual conditional. + * + * @param p Policy within which to look up types. + * @param cond Conditional to compare against. + * @param name Source boolean name from which to compare. + * @param flags If APOL_QUERY_REGEX bit is set, treat name as a + * regular expression. + * @param regex If using regexp comparison, the compiled regular + * expression to use; the pointer will be allocated space if regexp is + * legal. If NULL, then compile the regexp pattern given by name and + * cache it here. + * + * @return 1 If comparison succeeds, 0 if not; < 0 on error. + */ + int apol_compare_cond_expr(const apol_policy_t * p, const qpol_cond_t * cond, const char *name, unsigned int flags, + regex_t ** bool_regex); + +/** + * Determines if a level query matches a qpol_level_t, either + * the sensitivity name or any of its aliases. + * + * @param p Policy within which to look up types. + * @param level level datum to compare against. + * @param name Source target from which to compare. + * @param flags If APOL_QUERY_REGEX bit is set, treat name as a + * regular expression. + * @param regex If using regexp comparison, the compiled regular + * expression to use; the pointer will be allocated space if regexp is + * legal. If NULL, then compile the regexp pattern given by name and + * cache it here. + * + * @return 1 If comparison succeeds, 0 if not; < 0 on error. + */ + int apol_compare_level(const apol_policy_t * p, const qpol_level_t * level, const char *name, unsigned int flags, + regex_t ** level_regex); + +/** + * Determines if a category query matches a qpol_cat_t, either + * the category name or any of its aliases. + * + * @param p Policy within which to look up types. + * @param cat category datum to compare against. + * @param name Source target from which to compare. + * @param flags If APOL_QUERY_REGEX bit is set, treat name as a + * regular expression. + * @param regex If using regexp comparison, the compiled regular + * expression to use; the pointer will be allocated space if regexp is + * legal. If NULL, then compile the regexp pattern given by name and + * cache it here. + * + * @return 1 If comparison succeeds, 0 if not; < 0 on error. + */ + int apol_compare_cat(const apol_policy_t * p, const qpol_cat_t * cat, const char *name, unsigned int flags, + regex_t ** cat_regex); + +/** + * Convenience function that compares a qpol_context_t to a + * apol_context_t, based upon the MLS range match given by flags. If + * search is NULL then the comparison always succeeds. + * + * @param p Policy within which to look up types. + * @param target Target context to compare. + * @param name Source context from which to compare. + * @param flags Gives how to match MLS ranges within the contexts. + * + * @return 1 If comparison succeeds, 0 if not; < 0 on error. + */ + int apol_compare_context(const apol_policy_t * p, const qpol_context_t * target, const apol_context_t * search, + unsigned int flags); + +/** + * Given a type name, obtain its qpol_type_t pointer (relative to a + * policy). If the type is really its alias, get its primary instead. + * (Attributes are considered to be always primary.) + * + * @param p Policy in which to look up types. + * @param type_name Name of type to find. + * @param type Reference to where to store resulting pointer. + * + * @return 0 on success, < 0 on error. + */ + int apol_query_get_type(const apol_policy_t * p, const char *type_name, const qpol_type_t ** type); + +/** + * Given a symbol name (a type, attribute, alias, or a regular + * expression string), determine all types/attributes it matches. + * Return a vector of qpol_type_t that match. If regex is enabled, + * include all types/attributes that match the expression. If + * indirect is enabled, expand the candidiates within the vector (all + * attributes for a type, all types for an attribute), and then + * uniquify the vector. + * + * @param p Policy in which to look up types. + * @param symbol A string describing one or more type/attribute to + * which match. + * @param do_regex If non-zero, then treat symbol as a regular expression. + * @param do_indirect If non-zero, expand types to their attributes + * and attributes to their types. + * @param ta_flag Bit-wise or of (APOL_QUERY_SYMBOL_IS_TYPE, + * APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_SYMBOL_IS_BOTH) whether + * symbol should be matched against type names or attribute names. + * + * @return Vector of unique qpol_type_t pointers (relative to policy + * within p), or NULL upon error. Caller is responsible for calling + * apol_vector_destroy() afterwards. + */ + apol_vector_t *apol_query_create_candidate_type_list(const apol_policy_t * p, const char *symbol, int do_regex, + int do_indirect, unsigned int ta_flag); + +/** + * Given a symbol name (a type, attribute, alias, or a regular + * expression string), determine all types/attributes it matches. + * Return a vector of qpol_type_t that match. If regex is enabled, + * include all types/attributes that match the expression. If + * indirect is enabled, expand the candidiates within the vector (all + * attributes for a type, all types for an attribute), and then + * uniquify the vector. The list will include types needed for syntactic + * rule searching. + * + * @param p Policy in which to look up types. <b>Must be a source policy.</b> + * @param symbol A string describing one or more type/attribute to + * which match. + * @param do_regex If non-zero, then treat symbol as a regular expression. + * @param do_indirect If non-zero, expand types to their attributes + * and attributes to their types. + * @param ta_flag Bit-wise or of (APOL_QUERY_SYMBOL_IS_TYPE, + * APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_SYMBOL_IS_BOTH) whether + * symbol should be matched against type names or attribute names. + * + * @return Vector of unique qpol_type_t pointers (relative to policy + * within p), or NULL upon error. Caller is responsible for calling + * apol_vector_destroy() afterwards. + */ + apol_vector_t *apol_query_create_candidate_syn_type_list(const apol_policy_t * p, const char *symbol, int do_regex, + int do_indirect, unsigned int ta_flag); + +/** + * Given a symbol name (a role or a regular expression string), + * determine all roles it matches. Return a vector of qpol_role_t + * that match. If regex is enabled, include all role that + * match the expression. + * + * @param p Policy in which to look up roles. + * @param symbol A string describing one or more role to match. + * @param do_regex If non-zero, then treat symbol as a regular expression. + * + * @return Vector of unique qpol_role_t pointers (relative to policy + * within p), or NULL upon error. Caller is responsible for calling + * apol_vector_destroy() afterwards. + */ + apol_vector_t *apol_query_create_candidate_role_list(const apol_policy_t * p, char *symbol, int do_regex); + +/** + * Given a vector of object class strings, determine all of the + * classes it matches within the policy. Returns a vector of + * qpol_class_t that match. If a string does not match an object + * class within the policy then it is ignored. + * + * @param p Policy in which to look up classes. + * @param classes Vector of class strings to convert. + * + * @return Vector of unique qpol_class_t pointers (relative to policy + * within p), or NULL upon error. Caller is responsible for calling + * apol_vector_destroy() afterwards. + */ + apol_vector_t *apol_query_create_candidate_class_list(const apol_policy_t * p, apol_vector_t * classes); + +/** + * Given a type, return a vector of qpol_type_t pointers to which the + * type expands. If the type is just a type or an alias, the vector + * will have one element, pointing to the type's primary. If it was + * an attribute, the vector will have that attribute's types (but not + * the attribute itself). + * + * @param p Policy in which to look up types. + * @param t Type to expand. + * + * @return Vector of qpol_type_t pointers, or NULL upon error. Caller + * is responsible for calling apol_vector_destroy() afterwards. + */ + apol_vector_t *apol_query_expand_type(const apol_policy_t * p, const qpol_type_t * t); + +/** + * Object class and permission set. + * Contains the name of a class and a list of permissions + * used by analyses and complex searches to allow permissions + * to be specified on a per class basis. + */ + typedef struct apol_obj_perm apol_obj_perm_t; + +/** + * Allocate and return a new object permission set. + * @return a newly allocated object permission set or NULL on error. + * Caller is responsible for calling apol_obj_perm_free() to free + * memory used. + */ + apol_obj_perm_t *apol_obj_perm_create(void); + +/** + * Free the memory used by an object permission set. + * @param op the object permission set to free. + */ + void apol_obj_perm_free(void *op); + +/** + * Set the object class name for an object permission set. + * If already set free the previous name. + * @param op The object permission set for which to set the object name. + * @param obj_name New object name to set; this string will be duplicated + * by this call. If NULL only free existing name (if any). + * @return 0 on success and < 0 on failure; if the call fails, + * errno will be set and the original object permission set will be unchanged. + */ + int apol_obj_perm_set_obj_name(apol_obj_perm_t * op, const char *obj_name); + +/** + * Get the object class name from an object permission set. + * @param op The object permission set from which to get the class name. + * @return The class name or NULL if not set or error. The caller <b>should + * NOT</b> free the returned string. + */ + char *apol_obj_perm_get_obj_name(const apol_obj_perm_t * op); + +/** + * Add a permission to the permission list of an object permission set. + * @param op The object permission set to which to add the permission. + * @param perm Name of the permission to add, this string will be duplicated. + * If NULL clear all permissions. If the permission is already in the list + * nothing is done; + * @return 0 on success and < 0 on failure; if the call fails, + * errno will be set and the original object permission set will be unchanged. + */ + int apol_obj_perm_append_perm(apol_obj_perm_t * op, const char *perm); + +/** + * Get a vector of the permissions in an object permission set. + * @param op The object permission set from which to get the permissions. + * @return Vector (of type char *) of permission names; the caller + * <b>should NOT</b> destroy this vector. + */ + apol_vector_t *apol_obj_perm_get_perm_vector(const apol_obj_perm_t * op); + +/** + * Comparision function for use with vectors of object permission sets. + * @param a first object permission set. + * @param b second object permission set. + * @param policy apol policy from which the objects and permissions come. + * @return < 0, 0, or > 0 if the value of the class of a is less than, equal + * to, or greater than that of b respectively. + */ + int apol_obj_perm_compare_class(const void *a, const void *b, void *policy); + +/** + * Determine if a syntactic type set directly uses any of the types in v. + * @param p Policy from which the type set and types come. + * @param set Syntactic type set to check. + * @param v Vector of types (qpol_type_t) to find in set. + * @return 0 if no types in v appear in set, > 0 if at least one type + * was found, and < 0 if an error occurred. + */ + int apol_query_type_set_uses_types_directly(const apol_policy_t * p, const qpol_type_set_t * set, const apol_vector_t * v); + +/** + * Deallocate all space associated with a particular policy's permmap, + * including the pointer itself. Afterwards set the pointer to NULL. + * + * @param p Reference to an apol_permmap_t to destroy. + */ + void permmap_destroy(apol_permmap_t ** p); + +/** + * Destroy the domain transition table freeing all memory used. + * @param table Reference pointer to the table to be destroyed. + */ + void domain_trans_table_destroy(apol_domain_trans_table_t ** table); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libapol/src/policy-query.c b/libapol/src/policy-query.c new file mode 100644 index 0000000..18152fb --- /dev/null +++ b/libapol/src/policy-query.c @@ -0,0 +1,903 @@ +/** + * @file + * + * Provides a way for setools to make queries about different + * components of a policy. The caller obtains a query object, fills + * in its parameters, and then runs the query; it obtains a vector of + * results. Searches are conjunctive -- all fields of the search + * query must match for a datum to be added to the results query. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" + +#include <errno.h> +#include <regex.h> +#include <stdlib.h> +#include <string.h> + +/******************** misc helpers ********************/ + +void apol_regex_destroy(regex_t ** regex) +{ + if (*regex != NULL) { + regfree(*regex); + free(*regex); + *regex = NULL; + } +} + +int apol_query_set(const apol_policy_t * p, char **query_name, regex_t ** regex, const char *name) +{ + if (*query_name != name) { + if (regex != NULL) { + apol_regex_destroy(regex); + } + free(*query_name); + *query_name = NULL; + if (name != NULL && name[0] != '\0' && ((*query_name) = strdup(name)) == NULL) { + ERR(p, "%s", strerror(errno)); + return -1; + } + } + return 0; +} + +int apol_query_set_flag(const apol_policy_t * p __attribute__ ((unused)), unsigned int *flags, const int is_flag, int flag_value) +{ + if (is_flag) { + *flags |= flag_value; + } else { + *flags &= ~flag_value; + } + return 0; +} + +int apol_query_set_regex(const apol_policy_t * p, unsigned int *flags, const int is_regex) +{ + return apol_query_set_flag(p, flags, is_regex, APOL_QUERY_REGEX); +} + +/********************* comparison helpers *********************/ + +int apol_compare(const apol_policy_t * p, const char *target, const char *name, unsigned int flags, regex_t ** regex) +{ + if (name == NULL || *name == '\0') { + return 1; + } + char errbuf[1024] = { '\0' }; + if ((flags & APOL_QUERY_REGEX) && regex != NULL) { + if (*regex == NULL) { + if ((*regex = malloc(sizeof(**regex))) == NULL) { + free(*regex); + *regex = NULL; + ERR(p, "%s", strerror(ENOMEM)); + return -1; + } + int regretv = regcomp(*regex, name, REG_EXTENDED | REG_NOSUB); + if (regretv) { + regerror(regretv, *regex, errbuf, 1024); + free(*regex); + *regex = NULL; + ERR(p, "%s", errbuf); + return -1; + } + } + if (regexec(*regex, target, 0, NULL, 0) == 0) { + return 1; + } + return 0; + } else { + if (strcmp(target, name) == 0) { + return 1; + } + return 0; + } +} + +int apol_compare_iter(const apol_policy_t * p, qpol_iterator_t * iter, + const char *name, unsigned int flags, regex_t ** regex, int do_free) +{ + int compval; + if (name == NULL || *name == '\0') { + return 1; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + char *iter_name; + if (qpol_iterator_get_item(iter, (void **)&iter_name) < 0) { + return -1; + } + compval = apol_compare(p, iter_name, name, flags, regex); + if (do_free) + free(iter_name); + if (compval != 0) { + /* matched at least one name, or error */ + return compval; + } + } + /* no matches */ + return 0; +} + +int apol_compare_type(const apol_policy_t * p, const qpol_type_t * type, const char *name, unsigned int flags, + regex_t ** type_regex) +{ + const char *type_name; + int compval; + qpol_iterator_t *alias_iter = NULL; + if (qpol_type_get_name(p->p, type, &type_name) < 0) { + return -1; + } + compval = apol_compare(p, type_name, name, flags, type_regex); + if (compval != 0) { + return compval; + } + /* also check if the matches against one of the type's + * aliases */ + if (qpol_type_get_alias_iter(p->p, type, &alias_iter) < 0) { + return -1; + } + compval = apol_compare_iter(p, alias_iter, name, flags, type_regex, 0); + qpol_iterator_destroy(&alias_iter); + return compval; +} + +int apol_compare_permissive(const apol_policy_t * p, const qpol_permissive_t * permissive, const char *name, unsigned int flags, + regex_t ** permissive_regex) +{ + const char *permissive_name; + int compval; + + if (qpol_permissive_get_name(p->p, permissive, &permissive_name) < 0) { + return -1; + } + compval = apol_compare(p, permissive_name, name, flags, permissive_regex); + + return compval; +} + +int apol_compare_polcap(const apol_policy_t * p, const qpol_polcap_t * polcap, const char *name, unsigned int flags, + regex_t ** polcap_regex) +{ + const char *polcap_name; + int compval; + + if (qpol_polcap_get_name(p->p, polcap, &polcap_name) < 0) { + return -1; + } + compval = apol_compare(p, polcap_name, name, flags, polcap_regex); + + return compval; +} + +int apol_compare_cond_expr(const apol_policy_t * p, const qpol_cond_t * cond, const char *name, unsigned int flags, + regex_t ** bool_regex) +{ + qpol_iterator_t *expr_iter = NULL; + int compval = -1; + if (qpol_cond_get_expr_node_iter(p->p, cond, &expr_iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(expr_iter); qpol_iterator_next(expr_iter)) { + qpol_cond_expr_node_t *expr; + uint32_t expr_type; + qpol_bool_t *qbool; + const char *bool_name; + if (qpol_iterator_get_item(expr_iter, (void **)&expr) < 0 || + qpol_cond_expr_node_get_expr_type(p->p, expr, &expr_type) < 0) { + goto cleanup; + } + if (expr_type != QPOL_COND_EXPR_BOOL) { + continue; + } + if (qpol_cond_expr_node_get_bool(p->p, expr, &qbool) < 0 || qpol_bool_get_name(p->p, qbool, &bool_name) < 0) { + goto cleanup; + } + compval = apol_compare(p, bool_name, name, flags, bool_regex); + if (compval != 0) { /* catches both errors and success */ + goto cleanup; + } + } + compval = 0; + cleanup: + qpol_iterator_destroy(&expr_iter); + return compval; +} + +int apol_compare_level(const apol_policy_t * p, const qpol_level_t * level, const char *name, unsigned int flags, + regex_t ** level_regex) +{ + const char *level_name; + int compval; + qpol_iterator_t *alias_iter = NULL; + if (qpol_level_get_name(p->p, level, &level_name) < 0) { + return -1; + } + compval = apol_compare(p, level_name, name, flags, level_regex); + if (compval != 0) { + return compval; + } + /* also check if the matches against one of the sensitivity's + * aliases */ + if (qpol_level_get_alias_iter(p->p, level, &alias_iter) < 0) { + return -1; + } + compval = apol_compare_iter(p, alias_iter, name, flags, level_regex, 0); + qpol_iterator_destroy(&alias_iter); + return compval; +} + +int apol_compare_cat(const apol_policy_t * p, const qpol_cat_t * cat, const char *name, unsigned int flags, regex_t ** cat_regex) +{ + const char *cat_name; + int compval; + qpol_iterator_t *alias_iter = NULL; + if (qpol_cat_get_name(p->p, cat, &cat_name) < 0) { + return -1; + } + compval = apol_compare(p, cat_name, name, flags, cat_regex); + if (compval != 0) { + return compval; + } + /* also check if the matches against one of the category's + * aliases */ + if (qpol_cat_get_alias_iter(p->p, cat, &alias_iter) < 0) { + return -1; + } + compval = apol_compare_iter(p, alias_iter, name, flags, cat_regex, 0); + qpol_iterator_destroy(&alias_iter); + return compval; +} + +int apol_compare_context(const apol_policy_t * p, const qpol_context_t * target, const apol_context_t * search, unsigned int flags) +{ + apol_context_t *apol_context; + int retval; + if (search == NULL) { + return 1; + } + apol_context = apol_context_create_from_qpol_context(p, target); + retval = apol_context_compare(p, apol_context, search, flags); + apol_context_destroy(&apol_context); + return retval; +} + +/******************** other helpers ********************/ + +int apol_query_get_type(const apol_policy_t * p, const char *type_name, const qpol_type_t ** type) +{ + unsigned char isalias; + if (qpol_policy_get_type_by_name(p->p, type_name, type) < 0 || qpol_type_get_isalias(p->p, *type, &isalias) < 0) { + return -1; + } + if (isalias) { + const char *primary_name; + if (qpol_type_get_name(p->p, *type, &primary_name) < 0 || + qpol_policy_get_type_by_name(p->p, primary_name, type) < 0) { + return -1; + } + } + return 0; +} + +/** + * Append a non-aliased type to a vector. If the passed in type is an + * alias, find its primary type and append that instead. + * + * @param p Policy in which to look up types. + * @param v Vector in which append the non-aliased type. + * @param type Type or attribute to append. If this is an alias, + * append its primary. + * + * @return 0 on success, < 0 on error. + */ +static int apol_query_append_type(const apol_policy_t * p, apol_vector_t * v, const qpol_type_t * type) +{ + unsigned char isalias; + const qpol_type_t *real_type = type; + if (qpol_type_get_isalias(p->p, type, &isalias) < 0) { + return -1; + } + if (isalias) { + const char *primary_name; + if (qpol_type_get_name(p->p, type, &primary_name) < 0 || + qpol_policy_get_type_by_name(p->p, primary_name, &real_type) < 0) { + return -1; + } + } + if (apol_vector_append(v, (void *)real_type) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + return -1; + } + return 0; +} + +apol_vector_t *apol_query_create_candidate_type_list(const apol_policy_t * p, const char *symbol, int do_regex, int do_indirect, + unsigned int ta_flag) +{ + apol_vector_t *list = apol_vector_create(NULL); + const qpol_type_t *type; + regex_t *regex = NULL; + qpol_iterator_t *iter = NULL, *alias_iter = NULL; + int retval = -1, error = 0; + unsigned char isalias, isattr; + const char *type_name; + int compval; + size_t i, orig_vector_size; + + if (list == NULL) { + error = EINVAL; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + + if (ta_flag == 0 || (ta_flag & ~APOL_QUERY_SYMBOL_IS_BOTH)) { + error = EINVAL; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + + if (!do_regex && apol_query_get_type(p, symbol, &type) == 0) { + if (apol_query_append_type(p, list, type) < 0) { + error = errno; + goto cleanup; + } + } + + if (do_regex) { + if (qpol_policy_get_type_iter(p->p, &iter) < 0) { + error = errno; + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&type) < 0 || qpol_type_get_name(p->p, type, &type_name) < 0) { + error = errno; + goto cleanup; + } + compval = apol_compare(p, type_name, symbol, APOL_QUERY_REGEX, ®ex); + if (compval < 0) { + error = errno; + goto cleanup; + } + if (compval && apol_query_append_type(p, list, type)) { + error = errno; + goto cleanup; + } + if (compval) + continue; + if (qpol_type_get_alias_iter(p->p, type, &alias_iter) < 0) { + error = errno; + goto cleanup; + } + for (; !qpol_iterator_end(alias_iter); qpol_iterator_next(alias_iter)) { + if (qpol_iterator_get_item(alias_iter, (void **)&type_name) < 0) { + error = errno; + goto cleanup; + } + compval = apol_compare(p, type_name, symbol, APOL_QUERY_REGEX, ®ex); + if (compval < 0) { + error = errno; + goto cleanup; + } + if (compval && apol_query_append_type(p, list, type)) { + error = errno; + goto cleanup; + } + if (compval) + break; + } + qpol_iterator_destroy(&alias_iter); + } + qpol_iterator_destroy(&iter); + } + + /* prune to match ta_flag */ + for (i = 0; i < apol_vector_get_size(list); i++) { + type = (qpol_type_t *) apol_vector_get_element(list, i); + if (qpol_type_get_isattr(p->p, type, &isattr) < 0) { + error = errno; + goto cleanup; + } + if ((isattr && !(ta_flag & APOL_QUERY_SYMBOL_IS_ATTRIBUTE)) || (!isattr && !(ta_flag & APOL_QUERY_SYMBOL_IS_TYPE))) { + apol_vector_remove(list, i); + i--; + } + } + + if (do_indirect) { + orig_vector_size = apol_vector_get_size(list); + for (i = 0; i < orig_vector_size; i++) { + type = (qpol_type_t *) apol_vector_get_element(list, i); + if (qpol_type_get_isalias(p->p, type, &isalias) < 0 || qpol_type_get_isattr(p->p, type, &isattr) < 0) { + error = errno; + goto cleanup; + } + if (isalias) { + continue; + } + if ((isattr && + qpol_type_get_type_iter(p->p, type, &iter) < 0) || + (!isattr && qpol_type_get_attr_iter(p->p, type, &iter) < 0)) { + error = errno; + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&type) < 0) { + error = errno; + goto cleanup; + } + if (apol_query_append_type(p, list, type)) { + error = errno; + goto cleanup; + } + } + qpol_iterator_destroy(&iter); + } + } + + apol_vector_sort_uniquify(list, NULL, NULL); + retval = 0; + cleanup: + if (regex != NULL) { + regfree(regex); + free(regex); + } + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&alias_iter); + if (retval < 0) { + apol_vector_destroy(&list); + errno = error; + } + return list; +} + +apol_vector_t *apol_query_create_candidate_syn_type_list(const apol_policy_t * p, const char *symbol, int do_regex, int do_indirect, + unsigned int ta_flag) +{ + apol_vector_t *list = apol_vector_create(NULL); + const qpol_type_t *type; + regex_t *regex = NULL; + qpol_iterator_t *iter = NULL, *alias_iter = NULL; + int retval = -1, error = 0; + unsigned char isalias, isattr; + const char *type_name; + int compval; + size_t i, orig_vector_size; + + if (list == NULL) { + error = EINVAL; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + + if (!p || !qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_ATTRIB_NAMES) + || !qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_SYN_RULES)) { + error = EINVAL; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + + if (ta_flag == 0 || (ta_flag & ~APOL_QUERY_SYMBOL_IS_BOTH)) { + error = EINVAL; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + + if (!do_regex && apol_query_get_type(p, symbol, &type) == 0) { + if (apol_query_append_type(p, list, type) < 0) { + error = errno; + goto cleanup; + } + } + + if (do_regex) { + if (qpol_policy_get_type_iter(p->p, &iter) < 0) { + error = errno; + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&type) < 0 || qpol_type_get_name(p->p, type, &type_name) < 0) { + error = errno; + goto cleanup; + } + compval = apol_compare(p, type_name, symbol, APOL_QUERY_REGEX, ®ex); + if (compval < 0) { + error = errno; + goto cleanup; + } + if (compval && apol_query_append_type(p, list, type)) { + error = errno; + goto cleanup; + } + if (compval) + continue; + if (qpol_type_get_alias_iter(p->p, type, &alias_iter) < 0) { + error = errno; + goto cleanup; + } + for (; !qpol_iterator_end(alias_iter); qpol_iterator_next(alias_iter)) { + if (qpol_iterator_get_item(alias_iter, (void **)&type_name) < 0) { + error = errno; + goto cleanup; + } + compval = apol_compare(p, type_name, symbol, APOL_QUERY_REGEX, ®ex); + if (compval < 0) { + error = errno; + goto cleanup; + } + if (compval && apol_query_append_type(p, list, type)) { + error = errno; + goto cleanup; + } + if (compval) + break; + } + qpol_iterator_destroy(&alias_iter); + } + qpol_iterator_destroy(&iter); + } + + /* prune to match ta_flag */ + for (i = 0; i < apol_vector_get_size(list); i++) { + type = (qpol_type_t *) apol_vector_get_element(list, i); + if (qpol_type_get_isattr(p->p, type, &isattr) < 0) { + error = errno; + goto cleanup; + } + if ((isattr && !(ta_flag & APOL_QUERY_SYMBOL_IS_ATTRIBUTE)) || (!isattr && !(ta_flag & APOL_QUERY_SYMBOL_IS_TYPE))) { + apol_vector_remove(list, i); + i--; + } + } + + orig_vector_size = apol_vector_get_size(list); + for (i = 0; i < orig_vector_size; i++) { + type = (qpol_type_t *) apol_vector_get_element(list, i); + if (qpol_type_get_isalias(p->p, type, &isalias) < 0 || qpol_type_get_isattr(p->p, type, &isattr) < 0) { + error = errno; + goto cleanup; + } + if (isalias) { + continue; + } + if (!do_indirect && !isattr) + continue; + if ((isattr && qpol_type_get_type_iter(p->p, type, &iter) < 0) || + (!isattr && qpol_type_get_attr_iter(p->p, type, &iter) < 0)) { + error = errno; + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&type) < 0) { + error = errno; + goto cleanup; + } + if (apol_query_append_type(p, list, type)) { + error = errno; + goto cleanup; + } + } + qpol_iterator_destroy(&iter); + } + + apol_vector_sort_uniquify(list, NULL, NULL); + retval = 0; + cleanup: + if (regex != NULL) { + regfree(regex); + free(regex); + } + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&alias_iter); + if (retval < 0) { + apol_vector_destroy(&list); + list = NULL; + errno = error; + } + return list; +} + +apol_vector_t *apol_query_create_candidate_role_list(const apol_policy_t * p, char *symbol, int do_regex) +{ + apol_vector_t *list = apol_vector_create(NULL); + const qpol_role_t *role; + regex_t *regex = NULL; + qpol_iterator_t *iter = NULL; + int retval = -1; + + if (list == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + + if (!do_regex && qpol_policy_get_role_by_name(p->p, symbol, &role) == 0) { + if (apol_vector_append(list, (void *)role) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + if (do_regex) { + if (qpol_policy_get_role_iter(p->p, &iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + const char *role_name; + int compval; + if (qpol_iterator_get_item(iter, (void **)&role) < 0 || qpol_role_get_name(p->p, role, &role_name) < 0) { + goto cleanup; + } + compval = apol_compare(p, role_name, symbol, APOL_QUERY_REGEX, ®ex); + if (compval < 0) { + goto cleanup; + } + if (compval && apol_vector_append(list, (void *)role)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + qpol_iterator_destroy(&iter); + } + apol_vector_sort_uniquify(list, NULL, NULL); + retval = 0; + cleanup: + if (regex != NULL) { + regfree(regex); + free(regex); + } + qpol_iterator_destroy(&iter); + if (retval < 0) { + apol_vector_destroy(&list); + list = NULL; + } + return list; +} + +apol_vector_t *apol_query_create_candidate_class_list(const apol_policy_t * p, apol_vector_t * classes) +{ + apol_vector_t *list = apol_vector_create(NULL); + size_t i; + int retval = -1; + + if (list == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + + for (i = 0; i < apol_vector_get_size(classes); i++) { + char *class_string = (char *)apol_vector_get_element(classes, i); + const qpol_class_t *class; + if (qpol_policy_get_class_by_name(p->p, class_string, &class) == 0) { + if (apol_vector_append(list, (void *)class) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + } + apol_vector_sort_uniquify(list, NULL, NULL); + retval = 0; + cleanup: + if (retval < 0) { + apol_vector_destroy(&list); + list = NULL; + } + return list; +} + +apol_vector_t *apol_query_expand_type(const apol_policy_t * p, const qpol_type_t * t) +{ + apol_vector_t *v = NULL; + int retval = -1; + unsigned char isattr; + qpol_iterator_t *iter = NULL; + + if ((v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + if (qpol_type_get_isattr(p->p, t, &isattr) < 0) { + goto cleanup; + } + if (!isattr) { + if (apol_vector_append(v, (void *)t) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } else { + if (qpol_type_get_type_iter(p->p, t, &iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_type_t *type; + if (qpol_iterator_get_item(iter, (void **)&type) < 0) { + goto cleanup; + } + if (apol_vector_append(v, type) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + } + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + if (retval != 0) { + apol_vector_destroy(&v); + return NULL; + } + return v; +} + +/******** apol_obj_perm - set of an object with a list of permissions ********/ + +struct apol_obj_perm +{ + char *obj_class; /* name of object class */ + apol_vector_t *perms; /* vector of permission names */ +}; + +apol_obj_perm_t *apol_obj_perm_create(void) +{ + apol_obj_perm_t *op = calloc(1, sizeof(apol_obj_perm_t)); + if (!op) + return NULL; + + op->perms = apol_vector_create(free); + if (!(op->perms)) { + free(op); + return NULL; + } + + return op; +} + +void apol_obj_perm_free(void *op) +{ + apol_obj_perm_t *inop = (apol_obj_perm_t *) op; + if (inop != NULL) { + free(inop->obj_class); + apol_vector_destroy(&inop->perms); + free(inop); + } +} + +int apol_obj_perm_set_obj_name(apol_obj_perm_t * op, const char *obj_name) +{ + char *tmp = NULL; + + if (!op) { + errno = EINVAL; + return -1; + } + + if (obj_name) { + if (!(tmp = strdup(obj_name))) + return -1; + free(op->obj_class); + op->obj_class = tmp; + } else { + free(op->obj_class); + op->obj_class = NULL; + } + + return 0; +} + +char *apol_obj_perm_get_obj_name(const apol_obj_perm_t * op) +{ + if (!op) { + errno = EINVAL; + return NULL; + } + + return op->obj_class; +} + +int apol_obj_perm_append_perm(apol_obj_perm_t * op, const char *perm) +{ + char *tmp = NULL; + + if (!op) { + errno = EINVAL; + return -1; + } + + if (perm) { + if ((tmp = strdup(perm)) == NULL || (op->perms == NULL && (op->perms = apol_vector_create(free)) == NULL)) { + free(tmp); + return -1; + } + if (apol_vector_append_unique(op->perms, tmp, apol_str_strcmp, NULL) < 0) { + free(tmp); + return -1; + } + } else { + apol_vector_destroy(&op->perms); + } + + return 0; +} + +apol_vector_t *apol_obj_perm_get_perm_vector(const apol_obj_perm_t * op) +{ + if (!op) { + errno = EINVAL; + return NULL; + } + + return op->perms; +} + +int apol_obj_perm_compare_class(const void *a, const void *b, void *policy) +{ + const apol_obj_perm_t *opa = (const apol_obj_perm_t *)a; + const apol_obj_perm_t *opb = (const apol_obj_perm_t *)b; + apol_policy_t *p = (apol_policy_t *) policy; + const qpol_class_t *obja = NULL, *objb = NULL; + uint32_t a_val = 0, b_val = 0; + + qpol_policy_get_class_by_name(p->p, opa->obj_class, &obja); + qpol_policy_get_class_by_name(p->p, opb->obj_class, &objb); + qpol_class_get_value(p->p, obja, &a_val); + qpol_class_get_value(p->p, objb, &b_val); + + return (int)(a_val - b_val); +} + +int apol_query_type_set_uses_types_directly(const apol_policy_t * p, const qpol_type_set_t * set, const apol_vector_t * v) +{ + qpol_iterator_t *iter = NULL; + qpol_type_t *type = NULL; + size_t i; + uint32_t comp; + + if (!p || !set) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if (!v || !apol_vector_get_size(v)) + return 0; + + if (qpol_type_set_get_is_comp(p->p, set, &comp)) { + return -1; + } + if (comp) { + if (qpol_type_set_get_subtracted_types_iter(p->p, set, &iter)) { + return -1; + } + } else { + if (qpol_type_set_get_included_types_iter(p->p, set, &iter)) { + return -1; + } + } + + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_iterator_get_item(iter, (void **)&type); + if (!apol_vector_get_index(v, (void *)type, NULL, NULL, &i)) { + qpol_iterator_destroy(&iter); + return 1; + } + } + qpol_iterator_destroy(&iter); + + return 0; +} diff --git a/libapol/src/policy.c b/libapol/src/policy.c new file mode 100644 index 0000000..95ab7cd --- /dev/null +++ b/libapol/src/policy.c @@ -0,0 +1,217 @@ +/** + * @file + * + * Public interface for SELinux policies. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" + +#include <apol/perm-map.h> +#include <apol/domain-trans-analysis.h> + +#include <qpol/policy.h> +#include <qpol/policy_extend.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static void apol_handle_default_callback(void *varg __attribute__ ((unused)), const apol_policy_t * p + __attribute__ ((unused)), int level, const char *fmt, va_list va_args) +{ + switch (level) { + case APOL_MSG_INFO: + { + /* by default do not display these messages */ + return; + } + case APOL_MSG_WARN: + { + fprintf(stderr, "WARNING: "); + break; + } + case APOL_MSG_ERR: + default: + { + fprintf(stderr, "ERROR: "); + break; + } + } + vfprintf(stderr, fmt, va_args); + fprintf(stderr, "\n"); +} + +static void qpol_handle_route_to_callback(void *varg, const qpol_policy_t * policy + __attribute__ ((unused)), int level, const char *fmt, va_list ap) +{ + apol_policy_t *p = (apol_policy_t *) varg; + if (p == NULL) { + apol_handle_default_callback(NULL, NULL, level, fmt, ap); + } else if (p->msg_callback != NULL) { + p->msg_callback(p->msg_callback_arg, p, level, fmt, ap); + } +} + +apol_policy_t *apol_policy_create_from_policy_path(const apol_policy_path_t * path, const int options, + apol_callback_fn_t msg_callback, void *varg) +{ + apol_policy_t *policy; + const char *primary_path; + int policy_type; + if (!path) { + errno = EINVAL; + return NULL; + } + + if (!(policy = calloc(1, sizeof(apol_policy_t)))) { + ERR(NULL, "%s", strerror(ENOMEM)); + return NULL; /* errno set by calloc */ + } + if (msg_callback != NULL) { + policy->msg_callback = msg_callback; + } else { + policy->msg_callback = apol_handle_default_callback; + } + policy->msg_callback_arg = varg; + primary_path = apol_policy_path_get_primary(path); + INFO(policy, "Loading policy %s.", primary_path); + policy_type = qpol_policy_open_from_file(primary_path, &policy->p, qpol_handle_route_to_callback, policy, options); + if (policy_type < 0) { + ERR(policy, "Unable to open policy %s.", primary_path); + apol_policy_destroy(&policy); + return NULL; /* qpol sets errno */ + } + policy->policy_type = policy_type; + + if (apol_policy_path_get_type(path) == APOL_POLICY_PATH_TYPE_MODULAR) { + if (!qpol_policy_has_capability(policy->p, QPOL_CAP_MODULES)) { + INFO(policy, "%s is not a base policy.", primary_path); + return policy; + } + const apol_vector_t *modules = apol_policy_path_get_modules(path); + size_t i; + for (i = 0; i < apol_vector_get_size(modules); i++) { + const char *module_path = apol_vector_get_element(modules, i); + qpol_module_t *mod = NULL; + INFO(policy, "Loading module %s.", module_path); + if (qpol_module_create_from_file(module_path, &mod)) { + ERR(policy, "Error loading module %s.", module_path); + apol_policy_destroy(&policy); + return NULL; + } + if (qpol_policy_append_module(policy->p, mod)) { + ERR(policy, "Error loading module %s.", module_path); + apol_policy_destroy(&policy); + qpol_module_destroy(&mod); + return NULL; + } + } + INFO(policy, "%s", "Linking modules into base policy."); + if (qpol_policy_rebuild(policy->p, options)) { + apol_policy_destroy(&policy); + return NULL; + } + } + return policy; +} + +void apol_policy_destroy(apol_policy_t ** policy) +{ + if (policy != NULL && *policy != NULL) { + qpol_policy_destroy(&((*policy)->p)); + permmap_destroy(&(*policy)->pmap); + domain_trans_table_destroy(&(*policy)->domain_trans_table); + free(*policy); + *policy = NULL; + } +} + +int apol_policy_get_policy_type(const apol_policy_t * policy) +{ + if (policy == NULL) { + errno = EINVAL; + return -1; + } + return policy->policy_type; +} + +qpol_policy_t *apol_policy_get_qpol(const apol_policy_t * policy) +{ + if (policy == NULL) { + errno = EINVAL; + return NULL; + } + return policy->p; +} + +int apol_policy_is_mls(const apol_policy_t * p) +{ + if (p == NULL) { + return -1; + } + return qpol_policy_has_capability(p->p, QPOL_CAP_MLS); +} + +char *apol_policy_get_version_type_mls_str(const apol_policy_t * p) +{ + unsigned int version; + char *policy_type, *mls, buf[64]; + if (qpol_policy_get_policy_version(p->p, &version) < 0) { + return NULL; + } + switch (p->policy_type) { + case QPOL_POLICY_KERNEL_SOURCE: + policy_type = "source"; + break; + case QPOL_POLICY_KERNEL_BINARY: + policy_type = "binary"; + break; + case QPOL_POLICY_MODULE_BINARY: + policy_type = "modular"; + break; + default: + policy_type = "unknown"; + break; + } + if (qpol_policy_has_capability(p->p, QPOL_CAP_MLS)) { + mls = "mls"; + } else { + mls = "non-mls"; + } + if (snprintf(buf, sizeof(buf), "v.%u (%s, %s)", version, policy_type, mls) == -1) { + return NULL; + } + return strdup(buf); +} + +void apol_handle_msg(const apol_policy_t * p, int level, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (p == NULL) { + apol_handle_default_callback(NULL, NULL, level, fmt, ap); + } else if (p->msg_callback != NULL) { + p->msg_callback(p->msg_callback_arg, p, level, fmt, ap); + } + va_end(ap); +} diff --git a/libapol/src/queue.c b/libapol/src/queue.c new file mode 100644 index 0000000..1a3de23 --- /dev/null +++ b/libapol/src/queue.c @@ -0,0 +1,125 @@ +/** + * @file + * + * This file is a copy of queue.h from NSA's CVS repository. It has + * been modified to follow the setools naming conventions. + * + * Author : Stephen Smalley (NSA), <sds@epoch.ncsc.mil> + * + * Implementation of the double-ended queue type. + */ + +#include <stdlib.h> +#include "queue.h" + +apol_queue_t *apol_queue_create(void) +{ + apol_queue_t *q; + + q = (apol_queue_t *) malloc(sizeof(apol_queue_t)); + if (q == NULL) + return NULL; + + q->head = q->tail = NULL; + + return q; +} + +int apol_queue_insert(apol_queue_t * q, void *element) +{ + apol_queue_node_t *newnode; + + if (!q) + return -1; + + newnode = (apol_queue_node_t *) malloc(sizeof(struct apol_queue_node)); + if (newnode == NULL) + return -1; + + newnode->element = element; + newnode->next = NULL; + + if (q->head == NULL) { + q->head = q->tail = newnode; + } else { + q->tail->next = newnode; + q->tail = newnode; + } + + return 0; +} + +int apol_queue_push(apol_queue_t * q, void *element) +{ + apol_queue_node_t *newnode; + + if (!q) + return -1; + + newnode = (apol_queue_node_t *) malloc(sizeof(apol_queue_node_t)); + if (newnode == NULL) + return -1; + + newnode->element = element; + newnode->next = NULL; + + if (q->head == NULL) { + q->head = q->tail = newnode; + } else { + newnode->next = q->head; + q->head = newnode; + } + + return 0; +} + +void *apol_queue_remove(apol_queue_t * q) +{ + apol_queue_node_t *node; + void *element; + + if (!q) + return NULL; + + if (q->head == NULL) + return NULL; + + node = q->head; + q->head = q->head->next; + if (q->head == NULL) + q->tail = NULL; + + element = node->element; + free(node); + + return element; +} + +void *apol_queue_head(apol_queue_t * q) +{ + if (!q) + return NULL; + + if (q->head == NULL) + return NULL; + + return q->head->element; +} + +void apol_queue_destroy(apol_queue_t ** q) +{ + apol_queue_node_t *p, *temp; + + if (!q || *q == NULL) + return; + + p = (*q)->head; + while (p != NULL) { + temp = p; + p = p->next; + free(temp); + } + + free(*q); + *q = NULL; +} diff --git a/libapol/src/queue.h b/libapol/src/queue.h new file mode 100644 index 0000000..56eb97b --- /dev/null +++ b/libapol/src/queue.h @@ -0,0 +1,86 @@ +/** + * @file + * + * This file is a copy of queue.h from NSA's CVS repository. It has + * been modified to follow the setools naming conventions. + * + * Author : Stephen Smalley, <sds@epoch.ncsc.mil> + * + * A double-ended queue is a singly linked list of + * elements of arbitrary type that may be accessed + * at either end. + */ + +#ifndef APOL_QUEUE_H +#define APOL_QUEUE_H + +typedef struct apol_queue_node +{ + void *element; + struct apol_queue_node *next; +} apol_queue_node_t; + +typedef struct apol_queue +{ + apol_queue_node_t *head; + apol_queue_node_t *tail; +} apol_queue_t; + +/** + * Allocate and return a new queue. The caller is responsible for + * calling apol_queue_destroy() upon the return value. + * + * @return A newly allocated queue, or NULL upon error. + */ +apol_queue_t *apol_queue_create(void); + +/** + * Adds an element to the end of a queue. + * + * @param q Queue to modify. + * @param element Element to append to the end. + * + * @return 0 on success, < 0 on error. + */ +int apol_queue_insert(apol_queue_t * q, void *element); + +/** + * Adds an element to the beginning of a queue. + * + * @param q Queue to modify. + * @param element Element to prepend to the beginning. + * + * @return 0 on success, < 0 on error. + */ +int apol_queue_push(apol_queue_t * q, void *element); + +/** + * Remove the first element from a queue and return the data; the + * queue is advanced afterwards. If the queue was empty then return + * NULL. + * + * @return First element of a queue, or NULL if nothing is there. + */ +void *apol_queue_remove(apol_queue_t * q); + +/** + * Return the data within the first element, but do not remove it from + * the queue. If the queue was empty then return NULL. + * + * @return First element of a queue, or NULL if nothing is there. + */ +void *apol_queue_head(apol_queue_t * q); + +/** + * Destroy the referenced queue, but <i>do not</i> attempt to free the + * data stored within. (The caller is responsible for doing that.) + * Afterwards set the referenced variable to NULL. If the variable is + * NULL then do nothing. + * + * @param Reference to a queue to destroy. + */ +void apol_queue_destroy(apol_queue_t ** q); + +#endif + +/* FLASK */ diff --git a/libapol/src/range_trans-query.c b/libapol/src/range_trans-query.c new file mode 100644 index 0000000..6ba80b7 --- /dev/null +++ b/libapol/src/range_trans-query.c @@ -0,0 +1,318 @@ +/** + * @file + * + * Provides a way for setools to make queries about range transition + * rules within a policy. The caller obtains a query object, fills in + * its parameters, and then runs the query; it obtains a vector of + * results. Searches are conjunctive -- all fields of the search + * query must match for a datum to be added to the results query. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" + +#include <errno.h> + +struct apol_range_trans_query +{ + char *source, *target; + apol_vector_t *classes; + apol_mls_range_t *range; + unsigned int flags; +}; + +int apol_range_trans_get_by_query(const apol_policy_t * p, const apol_range_trans_query_t * r, apol_vector_t ** v) +{ + qpol_iterator_t *iter = NULL; + apol_vector_t *source_list = NULL, *target_list = NULL, *class_list = NULL; + apol_mls_range_t *range = NULL; + int retval = -1, source_as_any = 0; + *v = NULL; + + if (r != NULL) { + if (r->source != NULL && + (source_list = + apol_query_create_candidate_type_list(p, r->source, r->flags & APOL_QUERY_REGEX, + r->flags & APOL_QUERY_SOURCE_INDIRECT, + APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) { + goto cleanup; + } + if ((r->flags & APOL_QUERY_SOURCE_AS_ANY) && r->source != NULL) { + target_list = source_list; + source_as_any = 1; + } else if (r->target != NULL && + (target_list = + apol_query_create_candidate_type_list(p, r->target, r->flags & APOL_QUERY_REGEX, + r->flags & APOL_QUERY_TARGET_INDIRECT, + APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) { + goto cleanup; + } + if (r->classes != NULL && + apol_vector_get_size(r->classes) > 0 && + (class_list = apol_query_create_candidate_class_list(p, r->classes)) == NULL) { + goto cleanup; + } + } + + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + if (qpol_policy_get_range_trans_iter(p->p, &iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_range_trans_t *rule; + const qpol_mls_range_t *mls_range; + int match_source = 0, match_target = 0, compval; + size_t i; + if (qpol_iterator_get_item(iter, (void **)&rule) < 0) { + goto cleanup; + } + if (source_list == NULL) { + match_source = 1; + } else { + const qpol_type_t *source_type; + if (qpol_range_trans_get_source_type(p->p, rule, &source_type) < 0) { + goto cleanup; + } + if (apol_vector_get_index(source_list, source_type, NULL, NULL, &i) == 0) { + match_source = 1; + } + } + + /* if source did not match, but treating source symbol + * as any field, then delay rejecting this rule until + * the target has been checked */ + if (!source_as_any && !match_source) { + continue; + } + + if (target_list == NULL || (source_as_any && match_source)) { + match_target = 1; + } else { + const qpol_type_t *target_type; + if (qpol_range_trans_get_target_type(p->p, rule, &target_type) < 0) { + goto cleanup; + } + if (apol_vector_get_index(target_list, target_type, NULL, NULL, &i) == 0) { + match_target = 1; + } + } + + if (!match_target) { + continue; + } + + if (class_list != NULL) { + const qpol_class_t *obj_class; + if (qpol_range_trans_get_target_class(p->p, rule, &obj_class) < 0) { + goto cleanup; + } + if (apol_vector_get_index(class_list, obj_class, NULL, NULL, &i) < 0) { + continue; + } + } + + if (qpol_range_trans_get_range(p->p, rule, &mls_range) < 0 || + (range = apol_mls_range_create_from_qpol_mls_range(p, mls_range)) == NULL) { + goto cleanup; + } + if (r) + compval = apol_mls_range_compare(p, range, r->range, r->flags); + else + compval = 1; + apol_mls_range_destroy(&range); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + + if (apol_vector_append(*v, rule)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + apol_vector_destroy(&source_list); + if (!source_as_any) { + apol_vector_destroy(&target_list); + } + apol_vector_destroy(&class_list); + qpol_iterator_destroy(&iter); + apol_mls_range_destroy(&range); + return retval; +} + +apol_range_trans_query_t *apol_range_trans_query_create(void) +{ + return calloc(1, sizeof(apol_range_trans_query_t)); +} + +void apol_range_trans_query_destroy(apol_range_trans_query_t ** r) +{ + if (*r != NULL) { + free((*r)->source); + free((*r)->target); + apol_vector_destroy(&(*r)->classes); + apol_mls_range_destroy(&((*r)->range)); + free(*r); + *r = NULL; + } +} + +int apol_range_trans_query_set_source(const apol_policy_t * p, apol_range_trans_query_t * r, const char *symbol, int is_indirect) +{ + apol_query_set_flag(p, &r->flags, is_indirect, APOL_QUERY_SOURCE_INDIRECT); + return apol_query_set(p, &r->source, NULL, symbol); +} + +int apol_range_trans_query_set_target(const apol_policy_t * p, apol_range_trans_query_t * r, const char *symbol, int is_indirect) +{ + apol_query_set_flag(p, &r->flags, is_indirect, APOL_QUERY_TARGET_INDIRECT); + return apol_query_set(p, &r->target, NULL, symbol); +} + +int apol_range_trans_query_append_class(const apol_policy_t * p, apol_range_trans_query_t * r, const char *obj_class) +{ + char *s = NULL; + if (obj_class == NULL) { + apol_vector_destroy(&r->classes); + } else if ((s = strdup(obj_class)) == NULL || (r->classes == NULL && (r->classes = apol_vector_create(free)) == NULL) + || apol_vector_append(r->classes, s) < 0) { + ERR(p, "%s", strerror(errno)); + free(s); + return -1; + } + return 0; +} + +int apol_range_trans_query_set_range(const apol_policy_t * p __attribute__ ((unused)), + apol_range_trans_query_t * r, apol_mls_range_t * range, unsigned int range_match) +{ + if (r->range != NULL) { + apol_mls_range_destroy(&r->range); + } + r->range = range; + r->flags = (r->flags & ~APOL_QUERY_FLAGS) | range_match; + return 0; +} + +int apol_range_trans_query_set_source_any(const apol_policy_t * p, apol_range_trans_query_t * r, int is_any) +{ + return apol_query_set_flag(p, &r->flags, is_any, APOL_QUERY_SOURCE_AS_ANY); +} + +int apol_range_trans_query_set_regex(const apol_policy_t * p, apol_range_trans_query_t * r, int is_regex) +{ + return apol_query_set_regex(p, &r->flags, is_regex); +} + +char *apol_range_trans_render(const apol_policy_t * policy, const qpol_range_trans_t * rule) +{ + char *tmp = NULL; + const char *tmp_name = NULL; + int error = 0; + size_t tmp_sz = 0; + const qpol_type_t *type = NULL; + const qpol_class_t *target_class = NULL; + const qpol_mls_range_t *range = NULL; + apol_mls_range_t *arange = NULL; + + if (!policy || !rule) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + + /* range_transition */ + if (apol_str_append(&tmp, &tmp_sz, "range_transition ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + return NULL; + } + + /* source type */ + if (qpol_range_trans_get_source_type(policy->p, rule, &type) || + qpol_type_get_name(policy->p, type, &tmp_name) || + apol_str_append(&tmp, &tmp_sz, tmp_name) || apol_str_append(&tmp, &tmp_sz, " ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + /* target type */ + if (qpol_range_trans_get_target_type(policy->p, rule, &type) || + qpol_type_get_name(policy->p, type, &tmp_name) || + apol_str_append(&tmp, &tmp_sz, tmp_name) || apol_str_append(&tmp, &tmp_sz, " : ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + /* target class */ + if (qpol_range_trans_get_target_class(policy->p, rule, &target_class) || + qpol_class_get_name(policy->p, target_class, &tmp_name) || + apol_str_append(&tmp, &tmp_sz, tmp_name) || apol_str_append(&tmp, &tmp_sz, " ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + /* range */ + if (qpol_range_trans_get_range(policy->p, rule, &range)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (!(arange = apol_mls_range_create_from_qpol_mls_range(policy, range))) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + char *tmp_range_str = NULL; + if (!(tmp_range_str = apol_mls_range_render(policy, arange))) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + apol_mls_range_destroy(&arange); + if (apol_str_append(&tmp, &tmp_sz, tmp_range_str) || apol_str_append(&tmp, &tmp_sz, ";")) { + free(tmp_range_str); + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + free(tmp_range_str); + return tmp; + + err: + apol_mls_range_destroy(&arange); + free(tmp); + errno = error; + return NULL; +} diff --git a/libapol/src/rbacrule-query.c b/libapol/src/rbacrule-query.c new file mode 100644 index 0000000..81b0c53 --- /dev/null +++ b/libapol/src/rbacrule-query.c @@ -0,0 +1,417 @@ +/** + * @file + * + * Provides a way for setools to make queries about type enforcement + * rules within a policy. The caller obtains a query object, fills in + * its parameters, and then runs the query; it obtains a vector of + * results. Searches are conjunctive -- all fields of the search + * query must match for a datum to be added to the results query. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" + +#include <errno.h> +#include <string.h> + +struct apol_role_allow_query +{ + char *source, *target; + unsigned int flags; +}; + +struct apol_role_trans_query +{ + char *source, *target, *default_role; + unsigned int flags; +}; + +/******************** (role) allow queries ********************/ + +int apol_role_allow_get_by_query(const apol_policy_t * p, const apol_role_allow_query_t * r, apol_vector_t ** v) +{ + qpol_iterator_t *iter = NULL; + apol_vector_t *source_list = NULL, *target_list = NULL; + int retval = -1, source_as_any = 0; + *v = NULL; + + if (r != NULL) { + if (r->source != NULL && + (source_list = apol_query_create_candidate_role_list(p, r->source, r->flags & APOL_QUERY_REGEX)) == NULL) { + goto cleanup; + } + if ((r->flags & APOL_QUERY_SOURCE_AS_ANY) && r->source != NULL) { + target_list = source_list; + source_as_any = 1; + } else if (r->target != NULL && + (target_list = apol_query_create_candidate_role_list(p, r->target, r->flags & APOL_QUERY_REGEX)) == NULL) + { + goto cleanup; + } + } + if (qpol_policy_get_role_allow_iter(p->p, &iter) < 0) { + goto cleanup; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_role_allow_t *rule; + int match_source = 0, match_target = 0; + size_t i; + if (qpol_iterator_get_item(iter, (void **)&rule) < 0) { + goto cleanup; + } + + if (source_list == NULL) { + match_source = 1; + } else { + const qpol_role_t *source_role; + if (qpol_role_allow_get_source_role(p->p, rule, &source_role) < 0) { + goto cleanup; + } + if (apol_vector_get_index(source_list, source_role, NULL, NULL, &i) == 0) { + match_source = 1; + } + } + + /* if source did not match, but treating source symbol + * as any field, then delay rejecting this rule until + * the target has been checked */ + if (!source_as_any && !match_source) { + continue; + } + + if (target_list == NULL || (source_as_any && match_source)) { + match_target = 1; + } else { + const qpol_role_t *target_role; + if (qpol_role_allow_get_target_role(p->p, rule, &target_role) < 0) { + goto cleanup; + } + if (apol_vector_get_index(target_list, target_role, NULL, NULL, &i) == 0) { + match_target = 1; + } + } + if (!match_target) { + continue; + } + + if (apol_vector_append(*v, rule)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + apol_vector_destroy(&source_list); + if (!source_as_any) { + apol_vector_destroy(&target_list); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_role_allow_query_t *apol_role_allow_query_create(void) +{ + return calloc(1, sizeof(apol_role_allow_query_t)); +} + +void apol_role_allow_query_destroy(apol_role_allow_query_t ** r) +{ + if (r != NULL && *r != NULL) { + free((*r)->source); + free((*r)->target); + free(*r); + *r = NULL; + } +} + +int apol_role_allow_query_set_source(const apol_policy_t * p, apol_role_allow_query_t * r, const char *role) +{ + return apol_query_set(p, &r->source, NULL, role); +} + +int apol_role_allow_query_set_target(const apol_policy_t * p, apol_role_allow_query_t * r, const char *role) +{ + return apol_query_set(p, &r->target, NULL, role); +} + +int apol_role_allow_query_set_source_any(const apol_policy_t * p, apol_role_allow_query_t * r, int is_any) +{ + return apol_query_set_flag(p, &r->flags, is_any, APOL_QUERY_SOURCE_AS_ANY); +} + +int apol_role_allow_query_set_regex(const apol_policy_t * p, apol_role_allow_query_t * r, int is_regex) +{ + return apol_query_set_regex(p, &r->flags, is_regex); +} + +char *apol_role_allow_render(const apol_policy_t * policy, const qpol_role_allow_t * rule) +{ + char *tmp = NULL; + const char *source_name = NULL, *target_name = NULL; + const qpol_role_t *role = NULL; + + if (!policy || !rule) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + + /* source role */ + if (qpol_role_allow_get_source_role(policy->p, rule, &role)) { + ERR(policy, "%s", strerror(errno)); + return NULL; + } + if (qpol_role_get_name(policy->p, role, &source_name)) { + ERR(policy, "%s", strerror(errno)); + return NULL; + } + + /* target role */ + if (qpol_role_allow_get_target_role(policy->p, rule, &role)) { + ERR(policy, "%s", strerror(errno)); + return NULL; + } + if (qpol_role_get_name(policy->p, role, &target_name)) { + ERR(policy, "%s", strerror(errno)); + return NULL; + } + + if (asprintf(&tmp, "allow %s %s;", source_name, target_name) < 0) { + ERR(policy, "%s", strerror(errno)); + return NULL; + } + + return tmp; +} + +/******************** role_transition queries ********************/ + +int apol_role_trans_get_by_query(const apol_policy_t * p, const apol_role_trans_query_t * r, apol_vector_t ** v) +{ + qpol_iterator_t *iter = NULL; + apol_vector_t *source_list = NULL, *target_list = NULL, *default_list = NULL; + int retval = -1, source_as_any = 0; + *v = NULL; + + if (r != NULL) { + if (r->source != NULL && + (source_list = apol_query_create_candidate_role_list(p, r->source, r->flags & APOL_QUERY_REGEX)) == NULL) { + goto cleanup; + } + if (r->target != NULL && + (target_list = + apol_query_create_candidate_type_list(p, r->target, r->flags & APOL_QUERY_REGEX, + r->flags & APOL_QUERY_TARGET_INDIRECT, + APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) { + goto cleanup; + } + if ((r->flags & APOL_QUERY_SOURCE_AS_ANY) && r->source != NULL) { + default_list = source_list; + source_as_any = 1; + } else if (r->default_role != NULL && + (default_list = + apol_query_create_candidate_role_list(p, r->default_role, r->flags & APOL_QUERY_REGEX)) == NULL) { + goto cleanup; + } + } + if (qpol_policy_get_role_trans_iter(p->p, &iter) < 0) { + goto cleanup; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_role_trans_t *rule; + int match_source = 0, match_target = 0, match_default = 0; + size_t i; + if (qpol_iterator_get_item(iter, (void **)&rule) < 0) { + goto cleanup; + } + + if (source_list == NULL) { + match_source = 1; + } else { + const qpol_role_t *source_role; + if (qpol_role_trans_get_source_role(p->p, rule, &source_role) < 0) { + goto cleanup; + } + if (apol_vector_get_index(source_list, source_role, NULL, NULL, &i) == 0) { + match_source = 1; + } + } + + /* if source did not match, but treating source symbol + * as any field, then delay rejecting this rule until + * the target and default have been checked */ + if (!source_as_any && !match_source) { + continue; + } + + if (target_list == NULL) { + match_target = 1; + } else { + const qpol_type_t *target_type; + if (qpol_role_trans_get_target_type(p->p, rule, &target_type) < 0) { + goto cleanup; + } + if (apol_vector_get_index(target_list, target_type, NULL, NULL, &i) == 0) { + match_target = 1; + } + } + if (!match_target) { + continue; + } + + if (default_list == NULL || (source_as_any && match_source)) { + match_default = 1; + } else { + const qpol_role_t *default_role; + if (qpol_role_trans_get_default_role(p->p, rule, &default_role) < 0) { + goto cleanup; + } + if (apol_vector_get_index(default_list, default_role, NULL, NULL, &i) == 0) { + match_default = 1; + } + } + if (!match_default) { + continue; + } + + if (apol_vector_append(*v, rule)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + apol_vector_destroy(&source_list); + apol_vector_destroy(&target_list); + if (!source_as_any) { + apol_vector_destroy(&default_list); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_role_trans_query_t *apol_role_trans_query_create(void) +{ + return calloc(1, sizeof(apol_role_trans_query_t)); +} + +void apol_role_trans_query_destroy(apol_role_trans_query_t ** r) +{ + if (r != NULL && *r != NULL) { + free((*r)->source); + free((*r)->target); + free((*r)->default_role); + free(*r); + *r = NULL; + } +} + +int apol_role_trans_query_set_source(const apol_policy_t * p, apol_role_trans_query_t * r, const char *role) +{ + return apol_query_set(p, &r->source, NULL, role); +} + +int apol_role_trans_query_set_target(const apol_policy_t * p, apol_role_trans_query_t * r, const char *type, int is_indirect) +{ + apol_query_set_flag(p, &r->flags, is_indirect, APOL_QUERY_TARGET_INDIRECT); + return apol_query_set(p, &r->target, NULL, type); +} + +int apol_role_trans_query_set_default(const apol_policy_t * p, apol_role_trans_query_t * r, const char *role) +{ + return apol_query_set(p, &r->default_role, NULL, role); +} + +int apol_role_trans_query_set_source_any(const apol_policy_t * p, apol_role_trans_query_t * r, int is_any) +{ + return apol_query_set_flag(p, &r->flags, is_any, APOL_QUERY_SOURCE_AS_ANY); +} + +int apol_role_trans_query_set_regex(const apol_policy_t * p, apol_role_trans_query_t * r, int is_regex) +{ + return apol_query_set_regex(p, &r->flags, is_regex); +} + +char *apol_role_trans_render(const apol_policy_t * policy, const qpol_role_trans_t * rule) +{ + char *tmp = NULL; + const char *source_name = NULL, *target_name = NULL, *default_name = NULL; + const qpol_role_t *role = NULL; + const qpol_type_t *type = NULL; + + if (!policy || !rule) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + + /* source role */ + if (qpol_role_trans_get_source_role(policy->p, rule, &role)) { + ERR(policy, "%s", strerror(errno)); + return NULL; + } + if (qpol_role_get_name(policy->p, role, &source_name)) { + ERR(policy, "%s", strerror(errno)); + return NULL; + } + + /* target type */ + if (qpol_role_trans_get_target_type(policy->p, rule, &type)) { + ERR(policy, "%s", strerror(errno)); + return NULL; + } + if (qpol_type_get_name(policy->p, type, &target_name)) { + ERR(policy, "%s", strerror(errno)); + return NULL; + } + + /* default role */ + if (qpol_role_trans_get_default_role(policy->p, rule, &role)) { + ERR(policy, "%s", strerror(errno)); + return NULL; + } + if (qpol_role_get_name(policy->p, role, &default_name)) { + ERR(policy, "%s", strerror(errno)); + return NULL; + } + + if (asprintf(&tmp, "role_transition %s %s %s;", source_name, target_name, default_name) < 0) { + ERR(policy, "%s", strerror(errno)); + return NULL; + } + return tmp; +} diff --git a/libapol/src/relabel-analysis.c b/libapol/src/relabel-analysis.c new file mode 100644 index 0000000..d1cab99 --- /dev/null +++ b/libapol/src/relabel-analysis.c @@ -0,0 +1,813 @@ +/** + * @file + * Implementation of the direct relabelling analysis. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" + +#include <errno.h> +#include <string.h> + +/* defines for mode */ +#define APOL_RELABEL_MODE_OBJ 0x01 +#define APOL_RELABEL_MODE_SUBJ 0x02 + +struct apol_relabel_analysis +{ + unsigned int mode, direction; + char *type, *result; + apol_vector_t *classes, *subjects; + regex_t *result_regex; +}; + +/** + * Results are in the form of a list of apol_relabel_result_t nodes. + * Each node has three sublists of apol_relabel_result_pair_t. + */ +struct apol_relabel_result +{ + apol_vector_t *to; + apol_vector_t *from; + apol_vector_t *both; + const qpol_type_t *type; +}; + +struct apol_relabel_result_pair +{ + const qpol_avrule_t *ruleA, *ruleB; + const qpol_type_t *intermed; +}; + +#define PERM_RELABELTO "relabelto" +#define PERM_RELABELFROM "relabelfrom" + +/******************** actual analysis rountines ********************/ + +/** + * Given an avrule, determine which relabel direction it has (to, + * from, or both). + * + * @param p Policy containing avrule. + * @param avrule Rule to examine. + * + * @return One of APOL_RELABEL_DIR_TO, APOL_RELABEL_DIR_FROM, + * APOL_RELABEL_DIR_BOTH, or < 0 if direction could not be determined. + */ +static int relabel_analysis_get_direction(const apol_policy_t * p, const qpol_avrule_t * avrule) +{ + qpol_iterator_t *iter; + int to = 0, from = 0, retval = -1; + + if (qpol_avrule_get_perm_iter(p->p, avrule, &iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + char *perm; + if (qpol_iterator_get_item(iter, (void **)&perm) < 0) { + goto cleanup; + } + if (strcmp(perm, PERM_RELABELTO) == 0) { + to = 1; + } else if (strcmp(perm, PERM_RELABELFROM) == 0) { + from = 1; + } + free(perm); + perm = NULL; + } + if (to && from) { + retval = APOL_RELABEL_DIR_BOTH; + } else if (to) { + retval = APOL_RELABEL_DIR_TO; + } else if (from) { + retval = APOL_RELABEL_DIR_FROM; + } + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} + +/** + * Given an apol_relabel_result_t node and a qpol_type_t, determine if + * the two match. + * + * @param a Pointer to a apol_relabel_result_t. + * @param b Pointer to a type. + * @param data (Unused). + * + * @return 0 if the result node's type matches the given type, + * non-zero if not. + */ +static int relabel_result_comp_func(const void *a, const void *b, void *data __attribute__ ((unused))) +{ + apol_relabel_result_t *r = (apol_relabel_result_t *) a; + qpol_type_t *t = (qpol_type_t *) b; + return (int)((char *)r->type - (char *)t); +} + +static void relabel_result_free(void *result) +{ + if (result != NULL) { + apol_relabel_result_t *r = (apol_relabel_result_t *) result; + apol_vector_destroy(&r->to); + apol_vector_destroy(&r->from); + apol_vector_destroy(&r->both); + free(result); + } +} + +/** + * Given a qpol_type_t pointer, find and return the first + * apol_relabel_result_t node within vector v that matches the type. + * If there does not exist a node with that type, then allocate a new + * one, append it to the vector, and return it. The caller is + * expected to eventually call apol_vector_destroy() upon the vector. + * + * @param p Policy, used for error handling. + * @param results A vector of apol_relabel_result_t nodes. + * @param type Target type to find. + * + * @return An apol_relabel_result_t node from which to append results, + * or NULL upon error. + */ +static apol_relabel_result_t *relabel_result_get_node(const apol_policy_t * p, apol_vector_t * results, const qpol_type_t * type) +{ + apol_relabel_result_t *result; + size_t i; + if (apol_vector_get_index(results, type, relabel_result_comp_func, NULL, &i) == 0) { + return (apol_relabel_result_t *) apol_vector_get_element(results, i); + } + /* make a new result node */ + if ((result = calloc(1, sizeof(*result))) == NULL || + (result->to = apol_vector_create(free)) == NULL || + (result->from = apol_vector_create(free)) == NULL || + (result->both = apol_vector_create(free)) == NULL || apol_vector_append(results, result) < 0) { + ERR(p, "%s", strerror(errno)); + relabel_result_free(result); + return NULL; + } + result->type = type; + return result; +} + +/** + * Given a vector of strings representing type names, allocate and + * return a vector of qpol_type_t pointers into the given policy for + * those types. If a type name is really an alias, obtain and store + * its primary instead. + * + * @param p Policy to which look up types + * @param v Vector of strings. + * + * @return A newly allocated apol_vector_t, which the caller must free + * with apol_vector_destroy(). If a type name was not found or upon + * other error return NULL. + */ +static apol_vector_t *relabel_analysis_get_type_vector(const apol_policy_t * p, const apol_vector_t * v) +{ + apol_vector_t *types = NULL; + size_t i; + int retval = -1; + + if ((types = apol_vector_create_with_capacity(apol_vector_get_size(v), NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(v); i++) { + char *s = (char *)apol_vector_get_element(v, i); + const qpol_type_t *type; + if (apol_query_get_type(p, s, &type) < 0) { + goto cleanup; + } + if (apol_vector_append(types, (void *)type)) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval == -1) { + apol_vector_destroy(&types); + return NULL; + } + return types; +} + +/** + * Given a type, see if it is an element within a vector of + * qpol_type_t pointers. If the type is really an attribute, also + * check if any of the attribute's types are a member of v. If v is + * NULL then the comparison always succeeds. + * + * @param p Policy to which look up types. + * @param v Target vector of qpol_type_t pointers. + * @param type Source type to find. + * + * @return 1 if type is a member of v, 0 if not, < 0 on error. + */ +static int relabel_analysis_compare_type_to_vector(const apol_policy_t * p, const apol_vector_t * v, const qpol_type_t * type) +{ + size_t i; + unsigned char isattr; + qpol_iterator_t *iter = NULL; + int retval = -1; + if (v == NULL || apol_vector_get_index(v, type, NULL, NULL, &i) == 0) { + retval = 1; /* found it */ + goto cleanup; + } + if (qpol_type_get_isattr(p->p, type, &isattr) < 0) { + goto cleanup; + } + if (!isattr) { /* not an attribute, so comparison failed */ + retval = 0; + goto cleanup; + } + if (qpol_type_get_type_iter(p->p, type, &iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_type_t *t; + if (qpol_iterator_get_item(iter, (void **)&t) < 0) { + goto cleanup; + } + if (apol_vector_get_index(v, t, NULL, NULL, &i) == 0) { + retval = 1; + goto cleanup; + } + } + retval = 0; /* no matches */ + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} + +/** + * Given two avrules, possbily append it to the object results vector + * onto the appropriate rules vector. The decision to actually append + * or not is dependent upon the filtering options stored within the + * relabel analysis object. + * + * @param p Policy containing avrule. + * @param r Relabel analysis query object, containing filtering options. + * @param ruleA First AV rule to add. + * @param ruleB Other AV rule to add. + * @param result Results vector being built. + * + * @return 0 on success, < 0 on error. + */ +static int append_avrules_to_object_vector(const apol_policy_t * p, + apol_relabel_analysis_t * r, + const qpol_avrule_t * ruleA, const qpol_avrule_t * ruleB, apol_vector_t * results) +{ + const qpol_type_t *sourceA, *sourceB, *target, *intermed; + unsigned char isattrA, isattrB; + apol_vector_t *target_v = NULL, *result_list; + size_t i; + apol_relabel_result_t *result; + apol_relabel_result_pair_t *pair = NULL; + int retval = -1, compval, dirA, dirB; + if (qpol_avrule_get_target_type(p->p, ruleB, &target) < 0 || (target_v = apol_query_expand_type(p, target)) == NULL) { + goto cleanup; + } + if (qpol_avrule_get_source_type(p->p, ruleA, &sourceA) < 0 || + qpol_avrule_get_source_type(p->p, ruleB, &sourceB) < 0 || + qpol_type_get_isattr(p->p, sourceA, &isattrA) < 0 || qpol_type_get_isattr(p->p, sourceB, &isattrB) < 0) { + goto cleanup; + } + /* If both rules use the same attribute, retain the attribute + * to minimize the number of results and to indicate that all + * types with that attribute have the permission to relabel. */ + if ((isattrA && isattrB) || !isattrA) { + intermed = sourceA; + } else { + intermed = sourceB; + } + for (i = 0; i < apol_vector_get_size(target_v); i++) { + target = (qpol_type_t *) apol_vector_get_element(target_v, i); + /* exclude if B(t) does not match search criteria */ + compval = apol_compare_type(p, target, r->type, 0, NULL); + if (compval < 0) { + goto cleanup; + } else if (compval == 1) { + continue; /* don't care about relabels to itself */ + } + compval = apol_compare_type(p, target, r->result, APOL_QUERY_REGEX, &r->result_regex); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + if ((result = relabel_result_get_node(p, results, target)) == NULL) { + goto cleanup; + } + if ((dirA = relabel_analysis_get_direction(p, ruleA)) < 0 || (dirB = relabel_analysis_get_direction(p, ruleB)) < 0) { + goto cleanup; + } + if ((pair = calloc(1, sizeof(*pair))) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (dirA == APOL_RELABEL_DIR_BOTH && dirB == APOL_RELABEL_DIR_BOTH) { + result_list = result->both; + pair->ruleA = ruleA; + pair->ruleB = ruleB; + } else if (dirA == APOL_RELABEL_DIR_FROM || dirB == APOL_RELABEL_DIR_TO) { + result_list = result->to; + pair->ruleA = ruleA; + pair->ruleB = ruleB; + } else { + result_list = result->from; + pair->ruleA = ruleB; + pair->ruleB = ruleA; + } + pair->intermed = intermed; + if ((apol_vector_append(result_list, pair)) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + pair = NULL; + } + retval = 0; + cleanup: + free(pair); + apol_vector_destroy(&target_v); + return retval; +} + +/** + * Search through sets av and bv, finding pairs of avrules that + * satisfy a relabel and adding those pairs to result vector v. + * + * @param p Policy containing avrules. + * @param r Relabel analysis query object. + * @param v Vector of apol_relabel_result_t nodes. + * @param av Vector of qpol_avrule_t pointers. + * @param bv Vector of qpol_avrule_t pointers. + * @param subjects_v Vector of permitted qpol_type_t subjects, or NULL + * to allow all types. + * + * @return 0 on success, < 0 upon error. + */ +static int relabel_analysis_matchup(const apol_policy_t * p, + apol_relabel_analysis_t * r, + apol_vector_t * av, apol_vector_t * bv, const apol_vector_t * subjects_v, apol_vector_t * v) +{ + const qpol_avrule_t *a_avrule, *b_avrule; + const qpol_type_t *a_source, *a_target, *b_source, *b_target, *start_type; + const qpol_class_t *a_class, *b_class; + apol_vector_t *start_v = NULL; + size_t i, j; + int compval, retval = -1; + + if (apol_query_get_type(p, r->type, &start_type) < 0) { + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(av); i++) { + a_avrule = apol_vector_get_element(av, i); + if (qpol_avrule_get_source_type(p->p, a_avrule, &a_source) < 0 || + qpol_avrule_get_target_type(p->p, a_avrule, &a_target) < 0 || + qpol_avrule_get_object_class(p->p, a_avrule, &a_class) < 0) { + goto cleanup; + } + compval = relabel_analysis_compare_type_to_vector(p, subjects_v, a_source); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + if ((start_v = apol_query_expand_type(p, a_source)) == NULL) { + goto cleanup; + } + + /* check if there exists a B s.t. B(s) = source and + * B(t) != r->type and B(o) = A(o) */ + for (j = 0; j < apol_vector_get_size(bv); j++) { + b_avrule = apol_vector_get_element(bv, j); + if (qpol_avrule_get_source_type(p->p, b_avrule, &b_source) < 0 || + qpol_avrule_get_target_type(p->p, b_avrule, &b_target) < 0 || + qpol_avrule_get_object_class(p->p, b_avrule, &b_class) < 0) { + goto cleanup; + } + if (relabel_analysis_compare_type_to_vector(p, start_v, b_source) != 1 || + b_target == start_type || a_class != b_class) { + continue; + } + if (append_avrules_to_object_vector(p, r, a_avrule, b_avrule, v) < 0) { + goto cleanup; + } + } + apol_vector_destroy(&start_v); + } + + retval = 0; + cleanup: + apol_vector_destroy(&start_v); + return retval; +} + +/** + * Get a list of allow rules, whose target type matches r->type and + * whose permission is <i>opposite</i> of the direction given (e.g., + * relabelfrom if given DIR_TO). Only include rules whose class is a + * member of r->classes and whose source is a member of subjects_v. + * + * @param p Policy to which look up rules. + * @param r Structure containing parameters for subject relabel analysis. + * @param v Target vector to which append discovered rules. + * @param direction Relabelling direction to search. + * @param subjects_v If not NULL, then a vector of qpol_type_t pointers. + * + * @return 0 on success, < 0 on error. + */ +static int relabel_analysis_object(const apol_policy_t * p, + apol_relabel_analysis_t * r, + apol_vector_t * v, unsigned int direction, const apol_vector_t * subjects_v) +{ + apol_avrule_query_t *a = NULL, *b = NULL; + apol_vector_t *a_rules = NULL, *b_rules = NULL; + char *perm1, *perm2; + size_t i; + int retval = -1; + + if (direction == APOL_RELABEL_DIR_TO) { + perm1 = PERM_RELABELFROM; + perm2 = PERM_RELABELTO; + } else { + perm1 = PERM_RELABELTO; + perm2 = PERM_RELABELFROM; + } + + if ((a = apol_avrule_query_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_avrule_query_set_rules(p, a, QPOL_RULE_ALLOW) < 0 || + apol_avrule_query_set_target(p, a, r->type, 1) < 0 || apol_avrule_query_append_perm(p, a, perm1) < 0) { + goto cleanup; + } + for (i = 0; r->classes != NULL && i < apol_vector_get_size(r->classes); i++) { + if (apol_avrule_query_append_class(p, a, apol_vector_get_element(r->classes, i)) < 0) { + goto cleanup; + } + } + if (apol_avrule_get_by_query(p, a, &a_rules) < 0) { + goto cleanup; + } + + if ((b = apol_avrule_query_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_avrule_query_set_rules(p, b, QPOL_RULE_ALLOW) < 0 || apol_avrule_query_append_perm(p, b, perm2) < 0) { + goto cleanup; + } + for (i = 0; r->classes != NULL && i < apol_vector_get_size(r->classes); i++) { + if (apol_avrule_query_append_class(p, b, apol_vector_get_element(r->classes, i)) < 0) { + goto cleanup; + } + } + if (apol_avrule_get_by_query(p, b, &b_rules) < 0) { + goto cleanup; + } + + if (relabel_analysis_matchup(p, r, a_rules, b_rules, subjects_v, v) < 0) { + goto cleanup; + } + retval = 0; + cleanup: + apol_avrule_query_destroy(&a); + apol_vector_destroy(&a_rules); + apol_avrule_query_destroy(&b); + apol_vector_destroy(&b_rules); + return retval; +} + +/** + * Given an avrule, possbily append it to the subject results vector + * onto the appropriate rules vector. The decision to actually append + * or not is dependent upon the filtering options stored within the + * relabel analysis object. + * + * @param p Policy containing avrule. + * @param r Relabel analysis query object, containing filtering options. + * @param avrule AV rule to add. + * @param result Results vector being built. + * + * @return 0 on success, < 0 on error. + */ +static int append_avrule_to_subject_vector(const apol_policy_t * p, + apol_relabel_analysis_t * r, const qpol_avrule_t * avrule, apol_vector_t * results) +{ + const qpol_type_t *target; + apol_vector_t *target_v = NULL, *result_list = NULL; + size_t i; + apol_relabel_result_t *result; + apol_relabel_result_pair_t *pair = NULL; + int retval = -1, dir, compval; + if ((dir = relabel_analysis_get_direction(p, avrule)) < 0) { + goto cleanup; + } + if (qpol_avrule_get_target_type(p->p, avrule, &target) < 0 || (target_v = apol_query_expand_type(p, target)) == NULL) { + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(target_v); i++) { + target = (qpol_type_t *) apol_vector_get_element(target_v, i); + compval = apol_compare_type(p, target, r->type, 0, NULL); + if (compval < 0) { + goto cleanup; + } else if (compval == 1) { + continue; /* don't care about relabels to itself */ + } + compval = apol_compare_type(p, target, r->result, APOL_QUERY_REGEX, &r->result_regex); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + if ((result = relabel_result_get_node(p, results, target)) == NULL) { + goto cleanup; + } + if ((pair = calloc(1, sizeof(*pair))) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + pair->ruleA = avrule; + pair->ruleB = NULL; + pair->intermed = NULL; + switch (dir) { + case APOL_RELABEL_DIR_TO: + result_list = result->to; + break; + case APOL_RELABEL_DIR_FROM: + result_list = result->from; + break; + case APOL_RELABEL_DIR_BOTH: + result_list = result->both; + break; + } + if ((apol_vector_append(result_list, pair)) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + pair = NULL; + } + retval = 0; + cleanup: + apol_vector_destroy(&target_v); + free(pair); + return retval; +} + +/** + * Get a list of all allow rules, whose source type matches r->type + * and whose permission list has either "relabelto" or "relabelfrom". + * Only include rules whose class is a member of r->classes. Add + * instances of those to the result vector. + * + * @param p Policy to which look up rules. + * @param r Structure containing parameters for subject relabel analysis. + * @param v Target vector to which append discovered rules. + * + * @return 0 on success, < 0 on error. + */ +static int relabel_analysis_subject(const apol_policy_t * p, apol_relabel_analysis_t * r, apol_vector_t * v) +{ + apol_avrule_query_t *a = NULL; + apol_vector_t *avrules_v = NULL; + const qpol_avrule_t *avrule; + size_t i; + int retval = -1; + + if ((a = apol_avrule_query_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_avrule_query_set_rules(p, a, QPOL_RULE_ALLOW) < 0 || + apol_avrule_query_set_source(p, a, r->type, 1) < 0 || + apol_avrule_query_append_perm(p, a, PERM_RELABELTO) < 0 || apol_avrule_query_append_perm(p, a, PERM_RELABELFROM) < 0) { + goto cleanup; + } + for (i = 0; r->classes != NULL && i < apol_vector_get_size(r->classes); i++) { + if (apol_avrule_query_append_class(p, a, apol_vector_get_element(r->classes, i)) < 0) { + goto cleanup; + } + } + if (apol_avrule_get_by_query(p, a, &avrules_v) < 0) { + goto cleanup; + } + + for (i = 0; i < apol_vector_get_size(avrules_v); i++) { + avrule = (qpol_avrule_t *) apol_vector_get_element(avrules_v, i); + if (append_avrule_to_subject_vector(p, r, avrule, v) < 0) { + goto cleanup; + } + } + + retval = 0; + cleanup: + apol_avrule_query_destroy(&a); + apol_vector_destroy(&avrules_v); + return retval; +} + +/******************** public functions below ********************/ + +int apol_relabel_analysis_do(const apol_policy_t * p, apol_relabel_analysis_t * r, apol_vector_t ** v) +{ + apol_vector_t *subjects_v = NULL; + const qpol_type_t *start_type; + int retval = -1; + *v = NULL; + + if (r->mode == 0 || r->type == NULL) { + ERR(p, "%s", strerror(EINVAL)); + goto cleanup; + } + if (apol_query_get_type(p, r->type, &start_type) < 0) { + goto cleanup; + } + + if ((*v = apol_vector_create(relabel_result_free)) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + + if (r->mode == APOL_RELABEL_MODE_OBJ) { + if (r->subjects != NULL && (subjects_v = relabel_analysis_get_type_vector(p, r->subjects)) == NULL) { + goto cleanup; + } + if ((r->direction & APOL_RELABEL_DIR_TO) && relabel_analysis_object(p, r, *v, APOL_RELABEL_DIR_TO, subjects_v) < 0) { + goto cleanup; + } + if ((r->direction & APOL_RELABEL_DIR_FROM) && + relabel_analysis_object(p, r, *v, APOL_RELABEL_DIR_FROM, subjects_v) < 0) { + goto cleanup; + } + } else { + if (relabel_analysis_subject(p, r, *v) < 0) { + goto cleanup; + } + } + + retval = 0; + cleanup: + apol_vector_destroy(&subjects_v); + if (retval != 0) { + apol_vector_destroy(v); + } + return retval; +} + +apol_relabel_analysis_t *apol_relabel_analysis_create(void) +{ + return calloc(1, sizeof(apol_relabel_analysis_t)); +} + +void apol_relabel_analysis_destroy(apol_relabel_analysis_t ** r) +{ + if (r != NULL && *r != NULL) { + free((*r)->type); + free((*r)->result); + apol_vector_destroy(&(*r)->classes); + apol_vector_destroy(&(*r)->subjects); + apol_regex_destroy(&(*r)->result_regex); + free(*r); + *r = NULL; + } +} + +int apol_relabel_analysis_set_dir(const apol_policy_t * p, apol_relabel_analysis_t * r, unsigned int dir) +{ + if (p == NULL || r == NULL) { + ERR(p, "%s", strerror(EINVAL)); + return -1; + } + + switch (dir) { + case APOL_RELABEL_DIR_BOTH: + case APOL_RELABEL_DIR_TO: + case APOL_RELABEL_DIR_FROM: + { + r->mode = APOL_RELABEL_MODE_OBJ; + r->direction = dir; + break; + } + case APOL_RELABEL_DIR_SUBJECT: + { + r->mode = APOL_RELABEL_MODE_SUBJ; + r->direction = APOL_RELABEL_DIR_BOTH; + break; + } + default: + { + ERR(p, "%s", strerror(EINVAL)); + return -1; + } + } + return 0; +} + +int apol_relabel_analysis_set_type(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *name) +{ + if (p == NULL || r == NULL || name == NULL) { + ERR(p, "%s", strerror(EINVAL)); + return -1; + } + return apol_query_set(p, &r->type, NULL, name); +} + +int apol_relabel_analysis_append_class(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *obj_class) +{ + char *s; + if (p == NULL || r == NULL) { + ERR(p, "%s", strerror(EINVAL)); + return -1; + } + if (obj_class == NULL) { + apol_vector_destroy(&r->classes); + } else if ((s = strdup(obj_class)) == NULL || (r->classes == NULL && (r->classes = apol_vector_create(free)) == NULL) + || apol_vector_append(r->classes, s) < 0) { + ERR(p, "%s", strerror(errno)); + return -1; + } + return 0; +} + +int apol_relabel_analysis_append_subject(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *subject) +{ + char *s; + if (p == NULL || r == NULL) { + ERR(p, "%s", strerror(EINVAL)); + return -1; + } + if (subject == NULL) { + apol_vector_destroy(&r->subjects); + } else if ((s = strdup(subject)) == NULL || + (r->subjects == NULL && (r->subjects = apol_vector_create(free)) == NULL) || + apol_vector_append(r->subjects, s) < 0) { + ERR(p, "%s", strerror(errno)); + return -1; + } + return 0; +} + +int apol_relabel_analysis_set_result_regex(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *result) +{ + return apol_query_set(p, &r->result, &r->result_regex, result); +} + +/******************** functions to access relabel results ********************/ + +const apol_vector_t *apol_relabel_result_get_to(const apol_relabel_result_t * r) +{ + return r->to; +} + +const apol_vector_t *apol_relabel_result_get_from(const apol_relabel_result_t * r) +{ + return r->from; +} + +const apol_vector_t *apol_relabel_result_get_both(const apol_relabel_result_t * r) +{ + return r->both; +} + +const qpol_type_t *apol_relabel_result_get_result_type(const apol_relabel_result_t * r) +{ + return r->type; +} + +const qpol_avrule_t *apol_relabel_result_pair_get_ruleA(const apol_relabel_result_pair_t * p) +{ + return p->ruleA; +} + +const qpol_avrule_t *apol_relabel_result_pair_get_ruleB(const apol_relabel_result_pair_t * p) +{ + return p->ruleB; +} + +const qpol_type_t *apol_relabel_result_pair_get_intermediate_type(const apol_relabel_result_pair_t * p) +{ + return p->intermed; +} diff --git a/libapol/src/render.c b/libapol/src/render.c new file mode 100644 index 0000000..4bc19b6 --- /dev/null +++ b/libapol/src/render.c @@ -0,0 +1,158 @@ +/** + * @file + * + * Routines to render various data structures used by libapol. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author David Windsor dwindsor@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <apol/context-query.h> +#include <apol/policy.h> +#include <apol/render.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#ifndef WORDS_BIGENDIAN +extern void swab(const void *from, void *to, ssize_t n); +#endif + +#if LINK_SHARED == 1 +__asm__(".symver apol_ipv4_addr_render_old,apol_ipv4_addr_render@"); +__asm__(".symver apol_ipv4_addr_render_new,apol_ipv4_addr_render@@VERS_4.1"); +#endif + +/** + * @brief Internal version of apol_ipv4_addr_render() version 4.1 + * + * Implementation of the exported function apol_ipv4_addr_render() + * for version 4.1; this symbol name is not exported. + */ +char *apol_ipv4_addr_render_new(const apol_policy_t * policydb, uint32_t addr[4]) +{ + char buf[40], *b; + unsigned char *p = (unsigned char *)&(addr[0]); + snprintf(buf, sizeof(buf), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + if ((b = strdup(buf)) == NULL) { + ERR(policydb, "%s", strerror(ENOMEM)); + } + return b; +} + +#if LINK_SHARED == 0 +char *apol_ipv4_addr_render(const apol_policy_t * policydb, uint32_t addr[4]) +{ + return apol_ipv4_addr_render_new(policydb, addr); +} +#endif + +/** + * @brief Internal version of apol_ipv4_addr_render() version 4.0 or earlier + * @deprecated use the 4.1 version. + * @see apol_ipv4_addr_render() + */ +char *apol_ipv4_addr_render_old(apol_policy_t * policydb, uint32_t addr) +{ + char buf[40], *b; + unsigned char *p = (unsigned char *)&addr; + snprintf(buf, sizeof(buf), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + if ((b = strdup(buf)) == NULL) { + ERR(policydb, "%s", strerror(ENOMEM)); + } + return b; +} + +char *apol_ipv6_addr_render(const apol_policy_t * policydb, uint32_t addr[4]) +{ + uint16_t tmp[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int i, sz = 0, retv; + char buf[40], *b; /* 8 * 4 hex digits + 7 * ':' + '\0' == max size of string */ + int contract = 0, prev_contr = 0, contr_idx_end = -1; + for (i = 0; i < 4; i++) { + uint32_t a; +#ifdef WORDS_BIGENDIAN + a = addr[i]; +#else + swab(addr + i, &a, sizeof(a)); +#endif + /* have to use division and mod here, so as to ignore + * host system's byte ordering */ + tmp[2 * i] = a % (1 << 16); + tmp[2 * i + 1] = a / (1 << 16); + } + + for (i = 0; i < 8; i++) { + if (tmp[i] == 0) { + contract++; + if (i == 7 && contr_idx_end == -1) + contr_idx_end = 8; + } else { + if (contract > prev_contr) { + contr_idx_end = i; + } + prev_contr = contract; + contract = 0; + } + } + + if (prev_contr > contract) + contract = prev_contr; + + for (i = 0; i < 8; i++) { + if (i == contr_idx_end - contract) { + retv = snprintf(buf + sz, 40 - sz, i ? ":" : "::"); + sz += retv; + } else if (i > contr_idx_end - contract && i < contr_idx_end) { + continue; + } else { + retv = snprintf(buf + sz, 40 - sz, i == 7 ? "%04x" : "%04x:", tmp[i]); + sz += retv; + } + } + + buf[sz] = '\0'; + if ((b = strdup(buf)) == NULL) { + ERR(policydb, "%s", strerror(ENOMEM)); + } + return b; +} + +char *apol_qpol_context_render(const apol_policy_t * p, const qpol_context_t * context) +{ + apol_context_t *c = NULL; + char *rendered_context; + + if (p == NULL || context == NULL) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + + if ((c = apol_context_create_from_qpol_context(p, context)) == NULL) { + return NULL; + } + rendered_context = apol_context_render(p, c); + apol_context_destroy(&c); + return rendered_context; +} diff --git a/libapol/src/role-query.c b/libapol/src/role-query.c new file mode 100644 index 0000000..aaafe65 --- /dev/null +++ b/libapol/src/role-query.c @@ -0,0 +1,167 @@ +/** + * @file + * + * Provides a way for setools to make queries about roles within a + * policy. The caller obtains a query object, fills in its + * parameters, and then runs the query; it obtains a vector of + * results. Searches are conjunctive -- all fields of the search + * query must match for a datum to be added to the results query. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" + +#include <errno.h> + +struct apol_role_query +{ + char *role_name, *type_name; + unsigned int flags; + regex_t *role_regex, *type_regex; +}; + +/******************** role queries ********************/ + +int apol_role_get_by_query(const apol_policy_t * p, apol_role_query_t * r, apol_vector_t ** v) +{ + qpol_iterator_t *iter = NULL, *type_iter = NULL; + int retval = -1, append_role; + *v = NULL; + if (qpol_policy_get_role_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_role_t *role; + if (qpol_iterator_get_item(iter, (void **)&role) < 0) { + goto cleanup; + } + append_role = 1; + if (r != NULL) { + const char *role_name; + int compval; + if (qpol_role_get_name(p->p, role, &role_name) < 0) { + goto cleanup; + } + compval = apol_compare(p, role_name, r->role_name, r->flags, &(r->role_regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + if (r->type_name == NULL || r->type_name[0] == '\0') { + goto end_of_query; + } + if (qpol_role_get_type_iter(p->p, role, &type_iter) < 0) { + goto cleanup; + } + append_role = 0; + for (; !qpol_iterator_end(type_iter); qpol_iterator_next(type_iter)) { + qpol_type_t *type; + if (qpol_iterator_get_item(type_iter, (void **)&type) < 0) { + goto cleanup; + } + compval = apol_compare_type(p, type, r->type_name, r->flags, &(r->type_regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 1) { + append_role = 1; + break; + } + } + qpol_iterator_destroy(&type_iter); + } + end_of_query: + if (append_role && apol_vector_append(*v, role)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&type_iter); + return retval; +} + +apol_role_query_t *apol_role_query_create(void) +{ + return calloc(1, sizeof(apol_role_query_t)); +} + +void apol_role_query_destroy(apol_role_query_t ** r) +{ + if (*r != NULL) { + free((*r)->role_name); + free((*r)->type_name); + apol_regex_destroy(&(*r)->role_regex); + apol_regex_destroy(&(*r)->type_regex); + free(*r); + *r = NULL; + } +} + +int apol_role_query_set_role(const apol_policy_t * p, apol_role_query_t * r, const char *name) +{ + return apol_query_set(p, &r->role_name, &r->role_regex, name); +} + +int apol_role_query_set_type(const apol_policy_t * p, apol_role_query_t * r, const char *name) +{ + return apol_query_set(p, &r->type_name, &r->type_regex, name); +} + +int apol_role_query_set_regex(const apol_policy_t * p, apol_role_query_t * r, int is_regex) +{ + return apol_query_set_regex(p, &r->flags, is_regex); +} + +int apol_role_has_type(const apol_policy_t * p, const qpol_role_t * r, const qpol_type_t * t) +{ + qpol_iterator_t *iter = NULL; + qpol_type_t *tmp_type; + uint32_t type_value, t_type_value; + int retval = -1; + + if (qpol_type_get_value(p->p, t, &t_type_value) < 0 || qpol_role_get_type_iter(p->p, r, &iter) < 0) { + goto cleanup; + } + + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_iterator_get_item(iter, (void **)(&tmp_type)); + qpol_type_get_value(p->p, tmp_type, &type_value); + if (t_type_value == type_value) { + retval = 1; + goto cleanup; + } + } + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} diff --git a/libapol/src/terule-query.c b/libapol/src/terule-query.c new file mode 100644 index 0000000..80a7eff --- /dev/null +++ b/libapol/src/terule-query.c @@ -0,0 +1,1049 @@ +/** + * @file + * + * Provides a way for setools to make queries about type enforcement + * rules within a policy. The caller obtains a query object, fills in + * its parameters, and then runs the query; it obtains a vector of + * results. Searches are conjunctive -- all fields of the search + * query must match for a datum to be added to the results query. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2008 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" +#include <apol/bst.h> +#include <qpol/policy_extend.h> +#include <errno.h> +#include <string.h> + +struct apol_terule_query +{ + char *source, *target, *default_type, *bool_name; + apol_vector_t *classes; + unsigned int rules; + unsigned int flags; +}; + +/** + * Common semantic rule selection routine used in get*rule_by_query. + * @param p Policy to search. + * @param v Vector of rules to populate (of type qpol_terule_t). + * @param rule_type Mask of rules to search. + * @param flags Query options as specified by the apol_terule_query. + * @param source_list If non-NULL, list of types to use as source. + * If NULL, accept all types. + * @param target_list If non-NULL, list of types to use as target. + * If NULL, accept all types. + * @param class_list If non-NULL, list of classes to use. + * If NULL, accept all classes. + * @param default_list If non-NULL, list of types to use as default. + * If NULL, accept all types. + * @param bool_name If non-NULL, find conditional rules affected by this boolean. + * If NULL, all rules will be considered (including unconditional rules). + * @return 0 on success and < 0 on failure. + */ +static int rule_select(const apol_policy_t * p, apol_vector_t * v, uint32_t rule_type, unsigned int flags, + const apol_vector_t * source_list, const apol_vector_t * target_list, const apol_vector_t * class_list, + const apol_vector_t * default_list, const char *bool_name) +{ + qpol_iterator_t *iter = NULL; + int only_enabled = flags & APOL_QUERY_ONLY_ENABLED; + int is_regex = flags & APOL_QUERY_REGEX; + int source_as_any = flags & APOL_QUERY_SOURCE_AS_ANY; + int retv = -1; + regex_t *bool_regex = NULL; + + if (qpol_policy_get_terule_iter(p->p, rule_type, &iter) < 0) { + goto cleanup; + } + + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_terule_t *rule; + uint32_t is_enabled; + const qpol_cond_t *cond = NULL; + int match_source = 0, match_target = 0, match_default = 0, match_bool = 0; + size_t i; + if (qpol_iterator_get_item(iter, (void **)&rule) < 0) { + goto cleanup; + } + + if (qpol_terule_get_is_enabled(p->p, rule, &is_enabled) < 0) { + goto cleanup; + } + if (!is_enabled && only_enabled) { + continue; + } + + if (bool_name != NULL) { + if (qpol_terule_get_cond(p->p, rule, &cond) < 0) { + goto cleanup; + } + if (cond == NULL) { + continue; /* skip unconditional rule */ + } + match_bool = apol_compare_cond_expr(p, cond, bool_name, is_regex, &bool_regex); + if (match_bool < 0) { + goto cleanup; + } else if (match_bool == 0) { + continue; + } + } + + if (source_list == NULL) { + match_source = 1; + } else { + const qpol_type_t *source_type; + if (qpol_terule_get_source_type(p->p, rule, &source_type) < 0) { + goto cleanup; + } + if (apol_vector_get_index(source_list, source_type, NULL, NULL, &i) == 0) { + match_source = 1; + } + } + + /* if source did not match, but treating source symbol + * as any field, then delay rejecting this rule until + * the target and default have been checked */ + if (!source_as_any && !match_source) { + continue; + } + + if (target_list == NULL || (source_as_any && match_source)) { + match_target = 1; + } else { + const qpol_type_t *target_type; + if (qpol_terule_get_target_type(p->p, rule, &target_type) < 0) { + goto cleanup; + } + if (apol_vector_get_index(target_list, target_type, NULL, NULL, &i) == 0) { + match_target = 1; + } + } + + if (!source_as_any && !match_target) { + continue; + } + + if (default_list == NULL || (source_as_any && match_source) || (source_as_any && match_target)) { + match_default = 1; + } else { + const qpol_type_t *default_type; + if (qpol_terule_get_default_type(p->p, rule, &default_type) < 0) { + goto cleanup; + } + if (apol_vector_get_index(default_list, default_type, NULL, NULL, &i) == 0) { + match_default = 1; + } + } + + if (!source_as_any && !match_default) { + continue; + } + /* at least one thing must match if source_as_any was given */ + if (source_as_any && (!match_source && !match_target && !match_default)) { + continue; + } + + if (class_list != NULL) { + const qpol_class_t *obj_class; + if (qpol_terule_get_object_class(p->p, rule, &obj_class) < 0) { + goto cleanup; + } + if (apol_vector_get_index(class_list, obj_class, NULL, NULL, &i) < 0) { + continue; + } + } + + if (apol_vector_append(v, rule)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retv = 0; + + cleanup: + apol_regex_destroy(&bool_regex); + qpol_iterator_destroy(&iter); + return retv; +} + +int apol_terule_get_by_query(const apol_policy_t * p, const apol_terule_query_t * t, apol_vector_t ** v) +{ + apol_vector_t *source_list = NULL, *target_list = NULL, *class_list = NULL, *default_list = NULL; + int retval = -1, source_as_any = 0, is_regex = 0; + char *bool_name = NULL; + *v = NULL; + unsigned int flags = 0; + + uint32_t rule_type = QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_MEMBER | QPOL_RULE_TYPE_CHANGE; + if (t != NULL) { + if (t->rules != 0) { + rule_type &= t->rules; + } + flags = t->flags; + is_regex = t->flags & APOL_QUERY_REGEX; + bool_name = t->bool_name; + if (t->source != NULL && + (source_list = + apol_query_create_candidate_type_list(p, t->source, is_regex, + t->flags & APOL_QUERY_SOURCE_INDIRECT, + ((t->flags & (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE)) / + APOL_QUERY_SOURCE_TYPE))) == NULL) { + goto cleanup; + } + if ((t->flags & APOL_QUERY_SOURCE_AS_ANY) && t->source != NULL) { + default_list = target_list = source_list; + source_as_any = 1; + } else { + if (t->target != NULL && + (target_list = + apol_query_create_candidate_type_list(p, t->target, is_regex, + t->flags & APOL_QUERY_TARGET_INDIRECT, + ((t-> + flags & (APOL_QUERY_TARGET_TYPE | APOL_QUERY_TARGET_ATTRIBUTE)) + / APOL_QUERY_TARGET_TYPE))) == NULL) { + goto cleanup; + } + if (t->default_type != NULL && + (default_list = + apol_query_create_candidate_type_list(p, t->default_type, is_regex, 0, + APOL_QUERY_SYMBOL_IS_TYPE)) == NULL) { + goto cleanup; + } + } + if (t->classes != NULL && + apol_vector_get_size(t->classes) > 0 && + (class_list = apol_query_create_candidate_class_list(p, t->classes)) == NULL) { + goto cleanup; + } + } + + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + + if (rule_select(p, *v, rule_type, flags, source_list, target_list, class_list, default_list, bool_name)) { + goto cleanup; + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + apol_vector_destroy(&source_list); + if (!source_as_any) { + apol_vector_destroy(&target_list); + apol_vector_destroy(&default_list); + } + apol_vector_destroy(&class_list); + return retval; +} + +int apol_syn_terule_get_by_query(const apol_policy_t * p, const apol_terule_query_t * t, apol_vector_t ** v) +{ + apol_vector_t *source_list = NULL, *target_list = NULL, *class_list = NULL, *default_list = NULL, *syn_v = NULL; + int retval = -1, source_as_any = 0, is_regex = 0; + char *bool_name = NULL; + *v = NULL; + size_t i; + unsigned int flags = 0; + + if (!p || !qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_SYN_RULES)) { + ERR(p, "%s", strerror(EINVAL)); + goto cleanup; + } + + uint32_t rule_type = QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_MEMBER | QPOL_RULE_TYPE_CHANGE; + if (t != NULL) { + if (t->rules != 0) { + rule_type &= t->rules; + } + flags = t->flags; + is_regex = t->flags & APOL_QUERY_REGEX; + bool_name = t->bool_name; + if (t->source != NULL && + (source_list = + apol_query_create_candidate_syn_type_list(p, t->source, is_regex, + t->flags & APOL_QUERY_SOURCE_INDIRECT, + ((t->flags & (APOL_QUERY_SOURCE_TYPE | + APOL_QUERY_SOURCE_ATTRIBUTE)) / + APOL_QUERY_SOURCE_TYPE))) == NULL) { + goto cleanup; + } + if ((t->flags & APOL_QUERY_SOURCE_AS_ANY) && t->source != NULL) { + default_list = target_list = source_list; + source_as_any = 1; + } else { + if (t->target != NULL && + (target_list = + apol_query_create_candidate_syn_type_list(p, t->target, is_regex, + t->flags & APOL_QUERY_TARGET_INDIRECT, + ((t->flags & (APOL_QUERY_TARGET_TYPE | + APOL_QUERY_TARGET_ATTRIBUTE)) + / APOL_QUERY_TARGET_TYPE))) == NULL) { + goto cleanup; + } + if (t->default_type != NULL && + (default_list = + apol_query_create_candidate_type_list(p, t->default_type, is_regex, 0, + APOL_QUERY_SYMBOL_IS_TYPE)) == NULL) { + goto cleanup; + } + } + if (t->classes != NULL && + apol_vector_get_size(t->classes) > 0 && + (class_list = apol_query_create_candidate_class_list(p, t->classes)) == NULL) { + goto cleanup; + } + } + + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + + if (rule_select(p, *v, rule_type, flags, source_list, target_list, class_list, default_list, bool_name)) { + goto cleanup; + } + + syn_v = apol_terule_list_to_syn_terules(p, *v); + if (!syn_v) { + goto cleanup; + } + apol_vector_destroy(v); + *v = syn_v; + syn_v = NULL; + + /* if source and target are indirect skip post filtering type sets */ + if ((t->flags & APOL_QUERY_SOURCE_INDIRECT) && (t->flags & (APOL_QUERY_TARGET_INDIRECT | APOL_QUERY_SOURCE_AS_ANY))) { + retval = 0; + goto cleanup; + } + /* if not searching by source, target, or default we are done */ + if (!source_list && !target_list && !default_list) { + retval = 0; + goto cleanup; + } + + if (source_list && !(t->flags & APOL_QUERY_SOURCE_INDIRECT)) { + apol_vector_destroy(&source_list); + source_list = + apol_query_create_candidate_type_list(p, t->source, is_regex, 0, + ((t->flags & (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE)) / + APOL_QUERY_SOURCE_TYPE)); + if (!source_list) + goto cleanup; + } + if (target_list && (source_as_any || !(t->flags & APOL_QUERY_TARGET_INDIRECT))) { + if (source_as_any) { + target_list = source_list; + } else { + apol_vector_destroy(&target_list); + target_list = + apol_query_create_candidate_type_list(p, t->target, is_regex, 0, + ((t->flags & (APOL_QUERY_SOURCE_TYPE | + APOL_QUERY_SOURCE_ATTRIBUTE)) / + APOL_QUERY_SOURCE_TYPE)); + if (!target_list) + goto cleanup; + } + } + if (source_as_any) { + default_list = source_list; + } + + for (i = 0; i < apol_vector_get_size(*v); i++) { + qpol_syn_terule_t *srule = apol_vector_get_element(*v, i); + const qpol_type_set_t *stypes = NULL, *ttypes = NULL; + const qpol_type_t *dflt = NULL; + size_t j; + int uses_source = 0, uses_target = 0, uses_default = 0; + qpol_syn_terule_get_source_type_set(p->p, srule, &stypes); + qpol_syn_terule_get_target_type_set(p->p, srule, &ttypes); + if (source_list && !(t->flags & APOL_QUERY_SOURCE_INDIRECT)) { + uses_source = apol_query_type_set_uses_types_directly(p, stypes, source_list); + if (uses_source < 0) + goto cleanup; + } else if (source_list && (t->flags & APOL_QUERY_SOURCE_INDIRECT)) { + uses_source = 1; + } else if (!source_list) { + uses_source = 1; + } + + if (target_list + && !(t->flags & APOL_QUERY_TARGET_INDIRECT || (source_as_any && t->flags & APOL_QUERY_SOURCE_INDIRECT))) { + uses_target = apol_query_type_set_uses_types_directly(p, ttypes, target_list); + if (uses_target < 0) + goto cleanup; + } else if (target_list + && (t->flags & APOL_QUERY_TARGET_INDIRECT || (source_as_any && t->flags & APOL_QUERY_SOURCE_INDIRECT))) { + uses_target = 1; + } else if (!target_list) { + uses_target = 1; + } + + if (default_list) { + qpol_syn_terule_get_default_type(p->p, srule, &dflt); + if (!apol_vector_get_index(default_list, (void *)dflt, NULL, NULL, &j)) + uses_default = 1; + } else if (!default_list) { + uses_default = 1; + } + + if (!((uses_source && uses_target && uses_default) + || (source_as_any && (uses_source || uses_target || uses_default)))) { + apol_vector_remove(*v, i); + i--; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + apol_vector_destroy(&syn_v); + apol_vector_destroy(&source_list); + if (!source_as_any) { + apol_vector_destroy(&target_list); + apol_vector_destroy(&default_list); + } + apol_vector_destroy(&class_list); + return retval; +} + +apol_terule_query_t *apol_terule_query_create(void) +{ + apol_terule_query_t *t = calloc(1, sizeof(apol_terule_query_t)); + if (t != NULL) { + t->rules = ~0U; + t->flags = + (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE | APOL_QUERY_TARGET_TYPE | + APOL_QUERY_TARGET_ATTRIBUTE); + } + return t; +} + +void apol_terule_query_destroy(apol_terule_query_t ** t) +{ + if (*t != NULL) { + free((*t)->source); + free((*t)->target); + free((*t)->default_type); + free((*t)->bool_name); + apol_vector_destroy(&(*t)->classes); + free(*t); + *t = NULL; + } +} + +int apol_terule_query_set_rules(const apol_policy_t * p __attribute__ ((unused)), apol_terule_query_t * t, unsigned int rules) +{ + if (rules != 0) { + t->rules = rules; + } else { + t->rules = ~0U; + } + return 0; +} + +int apol_terule_query_set_source(const apol_policy_t * p, apol_terule_query_t * t, const char *symbol, int is_indirect) +{ + apol_query_set_flag(p, &t->flags, is_indirect, APOL_QUERY_SOURCE_INDIRECT); + return apol_query_set(p, &t->source, NULL, symbol); +} + +int apol_terule_query_set_source_component(const apol_policy_t * p, apol_terule_query_t * t, unsigned int component) +{ + if (!t || !(component & APOL_QUERY_SYMBOL_IS_BOTH)) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + apol_query_set_flag(p, &t->flags, component & APOL_QUERY_SYMBOL_IS_TYPE, APOL_QUERY_SOURCE_TYPE); + apol_query_set_flag(p, &t->flags, component & APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_SOURCE_ATTRIBUTE); + return 0; +} + +int apol_terule_query_set_target(const apol_policy_t * p, apol_terule_query_t * t, const char *symbol, int is_indirect) +{ + apol_query_set_flag(p, &t->flags, is_indirect, APOL_QUERY_TARGET_INDIRECT); + return apol_query_set(p, &t->target, NULL, symbol); +} + +int apol_terule_query_set_target_component(const apol_policy_t * p, apol_terule_query_t * t, unsigned int component) +{ + if (!t || !(component & APOL_QUERY_SYMBOL_IS_BOTH)) { + ERR(p, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + apol_query_set_flag(p, &t->flags, component & APOL_QUERY_SYMBOL_IS_TYPE, APOL_QUERY_TARGET_TYPE); + apol_query_set_flag(p, &t->flags, component & APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_TARGET_ATTRIBUTE); + return 0; +} + +int apol_terule_query_set_default(const apol_policy_t * p, apol_terule_query_t * t, const char *symbol) +{ + return apol_query_set(p, &t->default_type, NULL, symbol); +} + +int apol_terule_query_append_class(const apol_policy_t * p, apol_terule_query_t * t, const char *obj_class) +{ + char *s = NULL; + if (obj_class == NULL) { + apol_vector_destroy(&t->classes); + } else if ((s = strdup(obj_class)) == NULL || (t->classes == NULL && (t->classes = apol_vector_create(free)) == NULL) + || apol_vector_append(t->classes, s) < 0) { + ERR(p, "%s", strerror(errno)); + free(s); + return -1; + } + return 0; +} + +int apol_terule_query_set_bool(const apol_policy_t * p, apol_terule_query_t * t, const char *bool_name) +{ + return apol_query_set(p, &t->bool_name, NULL, bool_name); +} + +int apol_terule_query_set_enabled(const apol_policy_t * p, apol_terule_query_t * t, int is_enabled) +{ + return apol_query_set_flag(p, &t->flags, is_enabled, APOL_QUERY_ONLY_ENABLED); +} + +int apol_terule_query_set_source_any(const apol_policy_t * p, apol_terule_query_t * t, int is_any) +{ + return apol_query_set_flag(p, &t->flags, is_any, APOL_QUERY_SOURCE_AS_ANY); +} + +int apol_terule_query_set_regex(const apol_policy_t * p, apol_terule_query_t * t, int is_regex) +{ + return apol_query_set_regex(p, &t->flags, is_regex); +} + +/** + * Comparison function for two syntactic terules. Will return -1 if + * a's line number is before b's, 1 if b is greater. + */ +static int apol_syn_terule_comp(const void *a, const void *b, void *data) +{ + qpol_syn_terule_t *r1 = (qpol_syn_terule_t *) a; + qpol_syn_terule_t *r2 = (qpol_syn_terule_t *) b; + apol_policy_t *p = (apol_policy_t *) data; + unsigned long num1, num2; + if (qpol_syn_terule_get_lineno(p->p, r1, &num1) < 0 || qpol_syn_terule_get_lineno(p->p, r2, &num2) < 0) { + return 0; + } + if (num1 != num2) { + return (int)num1 - (int)num2; + } + return (int)((char *)r1 - (char *)r2); +} + +apol_vector_t *apol_terule_to_syn_terules(const apol_policy_t * p, const qpol_terule_t * rule) +{ + apol_vector_t *v = NULL; + qpol_iterator_t *iter = NULL; + qpol_syn_terule_t *syn_terule; + int retval = -1, error = 0; + if (qpol_terule_get_syn_terule_iter(p->p, rule, &iter) < 0) { + error = errno; + goto cleanup; + } + if ((v = apol_vector_create(NULL)) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&syn_terule) < 0) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + if (apol_vector_append(v, syn_terule) < 0) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + } + apol_vector_sort_uniquify(v, apol_syn_terule_comp, (void *)p); + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + if (retval != 0) { + apol_vector_destroy(&v); + errno = error; + return NULL; + } + return v; +} + +apol_vector_t *apol_terule_list_to_syn_terules(const apol_policy_t * p, const apol_vector_t * rules) +{ + apol_bst_t *b = NULL; + qpol_terule_t *rule; + qpol_iterator_t *iter = NULL; + qpol_syn_terule_t *syn_terule; + apol_vector_t *v = NULL; + size_t i; + int retval = -1, error = 0; + + if ((b = apol_bst_create(apol_syn_terule_comp, NULL)) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(rules); i++) { + rule = apol_vector_get_element(rules, i); + if (qpol_terule_get_syn_terule_iter(p->p, rule, &iter) < 0) { + error = errno; + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&syn_terule) < 0) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + if (apol_bst_insert(b, syn_terule, (void *)p) < 0) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + } + qpol_iterator_destroy(&iter); + } + if ((v = apol_bst_get_vector(b, 1)) == NULL) { + error = errno; + ERR(p, "%s", strerror(error)); + goto cleanup; + } + retval = 0; + cleanup: + apol_bst_destroy(&b); + qpol_iterator_destroy(&iter); + if (retval != 0) { + errno = error; + return NULL; + } + return v; +} + +char *apol_terule_render(const apol_policy_t * policy, const qpol_terule_t * rule) +{ + char *tmp = NULL; + const char *tmp_name = NULL; + const char *rule_type_str; + int error = 0; + size_t tmp_sz = 0; + uint32_t rule_type = 0; + const qpol_type_t *type = NULL; + const qpol_class_t *obj_class = NULL; + + if (!policy || !rule) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + + /* rule type */ + if (qpol_terule_get_rule_type(policy->p, rule, &rule_type)) { + return NULL; + } + if (!(rule_type &= (QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE | QPOL_RULE_TYPE_MEMBER))) { + ERR(policy, "%s", "Invalid TE rule type"); + errno = EINVAL; + return NULL; + } + if (!(rule_type_str = apol_rule_type_to_str(rule_type))) { + ERR(policy, "%s", "Could not get TE rule type's string"); + errno = EINVAL; + return NULL; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", rule_type_str)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + /* source type */ + if (qpol_terule_get_source_type(policy->p, rule, &type)) { + error = errno; + goto err; + } + if (qpol_type_get_name(policy->p, type, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + /* target type */ + if (qpol_terule_get_target_type(policy->p, rule, &type)) { + error = errno; + goto err; + } + if (qpol_type_get_name(policy->p, type, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s : ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + /* object class */ + if (qpol_terule_get_object_class(policy->p, rule, &obj_class)) { + error = errno; + goto err; + } + if (qpol_class_get_name(policy->p, obj_class, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + /* default type */ + if (qpol_terule_get_default_type(policy->p, rule, &type)) { + error = errno; + goto err; + } + if (qpol_type_get_name(policy->p, type, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s;", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + return tmp; + + err: + free(tmp); + errno = error; + return NULL; +} + +char *apol_syn_terule_render(const apol_policy_t * policy, const qpol_syn_terule_t * rule) +{ + char *tmp = NULL; + const char *tmp_name = NULL; + const char *rule_type_str; + int error = 0; + uint32_t rule_type = 0, star = 0, comp = 0; + const qpol_type_t *type = NULL; + const qpol_class_t *obj_class = NULL; + qpol_iterator_t *iter = NULL, *iter2 = NULL; + size_t tmp_sz = 0, iter_sz = 0, iter2_sz = 0; + const qpol_type_set_t *set = NULL; + + if (!policy || !rule) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + + /* rule type */ + if (qpol_syn_terule_get_rule_type(policy->p, rule, &rule_type)) { + return NULL; + } + if (!(rule_type &= (QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE | QPOL_RULE_TYPE_MEMBER))) { + ERR(policy, "%s", "Invalid TE rule type"); + errno = EINVAL; + return NULL; + } + if (!(rule_type_str = apol_rule_type_to_str(rule_type))) { + ERR(policy, "%s", "Could not get TE rule type's string"); + errno = EINVAL; + return NULL; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", rule_type_str)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + /* source type set */ + if (qpol_syn_terule_get_source_type_set(policy->p, rule, &set)) { + error = errno; + goto err; + } + if (qpol_type_set_get_is_star(policy->p, set, &star)) { + error = errno; + goto err; + } + if (star) { + if (apol_str_append(&tmp, &tmp_sz, "* ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } else { + if (qpol_type_set_get_is_comp(policy->p, set, &comp)) { + error = errno; + goto err; + } + if (comp) { + if (apol_str_append(&tmp, &tmp_sz, "~")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + if (qpol_type_set_get_included_types_iter(policy->p, set, &iter)) { + error = errno; + goto err; + } + if (qpol_type_set_get_subtracted_types_iter(policy->p, set, &iter2)) { + error = errno; + goto err; + } + if (qpol_iterator_get_size(iter, &iter_sz) || qpol_iterator_get_size(iter2, &iter2_sz)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (iter_sz + iter2_sz > 1) { + if (apol_str_append(&tmp, &tmp_sz, "{ ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&type)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (qpol_type_get_name(policy->p, type, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + for (; !qpol_iterator_end(iter2); qpol_iterator_next(iter2)) { + if (qpol_iterator_get_item(iter2, (void **)&type)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (qpol_type_get_name(policy->p, type, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "-%s ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&iter2); + if (iter_sz + iter2_sz > 1) { + if (apol_str_append(&tmp, &tmp_sz, "} ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + } + + /* target type set */ + if (qpol_syn_terule_get_target_type_set(policy->p, rule, &set)) { + error = errno; + goto err; + } + if (qpol_type_set_get_is_star(policy->p, set, &star)) { + error = errno; + goto err; + } + if (star) { + if (apol_str_append(&tmp, &tmp_sz, "* ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } else { + if (qpol_type_set_get_is_comp(policy->p, set, &comp)) { + error = errno; + goto err; + } + if (comp) { + if (apol_str_append(&tmp, &tmp_sz, "~")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + if (qpol_type_set_get_included_types_iter(policy->p, set, &iter)) { + error = errno; + goto err; + } + if (qpol_type_set_get_subtracted_types_iter(policy->p, set, &iter2)) { + error = errno; + goto err; + } + if (qpol_iterator_get_size(iter, &iter_sz) || qpol_iterator_get_size(iter2, &iter2_sz)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (iter_sz + iter2_sz > 1) { + if (apol_str_append(&tmp, &tmp_sz, "{ ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&type)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (qpol_type_get_name(policy->p, type, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + for (; !qpol_iterator_end(iter2); qpol_iterator_next(iter2)) { + if (qpol_iterator_get_item(iter2, (void **)&type)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (qpol_type_get_name(policy->p, type, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "-%s ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&iter2); + if (iter_sz + iter2_sz > 1) { + if (apol_str_append(&tmp, &tmp_sz, "} ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + } + + if (apol_str_append(&tmp, &tmp_sz, ": ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + /* object classes */ + if (qpol_syn_terule_get_class_iter(policy->p, rule, &iter)) { + error = errno; + goto err; + } + if (qpol_iterator_get_size(iter, &iter_sz)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (iter_sz > 1) { + if (apol_str_append(&tmp, &tmp_sz, "{ ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&obj_class)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + if (qpol_class_get_name(policy->p, obj_class, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + qpol_iterator_destroy(&iter); + if (iter_sz > 1) { + if (apol_str_append(&tmp, &tmp_sz, "} ")) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + } + + /* default type */ + if (qpol_syn_terule_get_default_type(policy->p, rule, &type)) { + error = errno; + goto err; + } + if (qpol_type_get_name(policy->p, type, &tmp_name)) { + error = errno; + goto err; + } + if (apol_str_appendf(&tmp, &tmp_sz, "%s;", tmp_name)) { + error = errno; + ERR(policy, "%s", strerror(error)); + goto err; + } + + return tmp; + + err: + free(tmp); + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&iter2); + errno = error; + return NULL; +} diff --git a/libapol/src/type-query.c b/libapol/src/type-query.c new file mode 100644 index 0000000..7ead836 --- /dev/null +++ b/libapol/src/type-query.c @@ -0,0 +1,202 @@ +/** + * @file + * + * Provides a way for setools to make queries about types and + * attributes within a policy. The caller obtains a query object, + * fills in its parameters, and then runs the query; it obtains a + * vector of results. Searches are conjunctive -- all fields of the + * search query must match for a datum to be added to the results + * query. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" + +#include <errno.h> + +struct apol_type_query +{ + char *type_name; + unsigned int flags; + regex_t *regex; +}; + +struct apol_attr_query +{ + char *attr_name; + unsigned int flags; + regex_t *regex; +}; + +/******************** type queries ********************/ + +int apol_type_get_by_query(const apol_policy_t * p, apol_type_query_t * t, apol_vector_t ** v) +{ + qpol_iterator_t *iter; + int retval = -1; + *v = NULL; + if (qpol_policy_get_type_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + const qpol_type_t *type; + unsigned char isattr, isalias; + if (qpol_iterator_get_item(iter, (void **)&type) < 0) { + goto cleanup; + } + if (qpol_type_get_isattr(p->p, type, &isattr) < 0 || qpol_type_get_isalias(p->p, type, &isalias) < 0) { + goto cleanup; + } + if (isattr || isalias) { + continue; + } + if (t != NULL) { + int compval = apol_compare_type(p, + type, t->type_name, + t->flags, &(t->regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + } + if (apol_vector_append(*v, (void *)type)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_type_query_t *apol_type_query_create(void) +{ + return calloc(1, sizeof(apol_type_query_t)); +} + +void apol_type_query_destroy(apol_type_query_t ** t) +{ + if (*t != NULL) { + free((*t)->type_name); + apol_regex_destroy(&(*t)->regex); + free(*t); + *t = NULL; + } +} + +int apol_type_query_set_type(const apol_policy_t * p, apol_type_query_t * t, const char *name) +{ + return apol_query_set(p, &t->type_name, &t->regex, name); +} + +int apol_type_query_set_regex(const apol_policy_t * p, apol_type_query_t * t, int is_regex) +{ + return apol_query_set_regex(p, &t->flags, is_regex); +} + +/******************** attribute queries ********************/ + +int apol_attr_get_by_query(const apol_policy_t * p, apol_attr_query_t * a, apol_vector_t ** v) +{ + qpol_iterator_t *iter; + int retval = -1; + *v = NULL; + if (qpol_policy_get_type_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_type_t *type; + unsigned char isattr, isalias; + if (qpol_iterator_get_item(iter, (void **)&type) < 0) { + goto cleanup; + } + if (qpol_type_get_isattr(p->p, type, &isattr) < 0 || qpol_type_get_isalias(p->p, type, &isalias) < 0) { + goto cleanup; + } + if (!isattr || isalias) { + continue; + } + if (a != NULL) { + const char *attr_name; + int compval; + if (qpol_type_get_name(p->p, type, &attr_name) < 0) { + goto cleanup; + } + compval = apol_compare(p, attr_name, a->attr_name, a->flags, &(a->regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + } + if (apol_vector_append(*v, type)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + return retval; +} + +apol_attr_query_t *apol_attr_query_create(void) +{ + return calloc(1, sizeof(apol_attr_query_t)); +} + +void apol_attr_query_destroy(apol_attr_query_t ** a) +{ + if (*a != NULL) { + free((*a)->attr_name); + apol_regex_destroy(&(*a)->regex); + free(*a); + *a = NULL; + } +} + +int apol_attr_query_set_attr(const apol_policy_t * p, apol_attr_query_t * a, const char *name) +{ + return apol_query_set(p, &a->attr_name, &a->regex, name); +} + +int apol_attr_query_set_regex(const apol_policy_t * p, apol_attr_query_t * a, int is_regex) +{ + return apol_query_set_regex(p, &a->flags, is_regex); +} diff --git a/libapol/src/types-relation-analysis.c b/libapol/src/types-relation-analysis.c new file mode 100644 index 0000000..79203d9 --- /dev/null +++ b/libapol/src/types-relation-analysis.c @@ -0,0 +1,1143 @@ +/** + * @file + * Implementation of the two-types relationship analysis. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2005-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" +#include "domain-trans-analysis-internal.h" +#include "infoflow-analysis-internal.h" + +#include <errno.h> +#include <string.h> + +struct apol_types_relation_analysis +{ + char *typeA, *typeB; + unsigned int analyses; +}; + +struct apol_types_relation_result +{ + /** vector of qpol_type_t pointers */ + apol_vector_t *attribs; + /** vector of qpol_role_t pointers */ + apol_vector_t *roles; + /** vector of qpol_user_t pointers */ + apol_vector_t *users; + /** vector af apol_types_relation_access, rules that A has in common with B */ + apol_vector_t *simA; + /** vector af apol_types_relation_access, rules that B has in common with A */ + apol_vector_t *simB; + /** vector af apol_types_relation_access, types that A has that B does not */ + apol_vector_t *disA; + /** vector af apol_types_relation_access, types that B has that A does not */ + apol_vector_t *disB; + /** vector of qpol_avrule_t pointers */ + apol_vector_t *allows; + /** vector of qpol_terule_t pointers */ + apol_vector_t *types; + /** vector of apol_infoflow_result_t */ + apol_vector_t *dirflows; + /** vector of apol_infoflow_result_t from type A to B */ + apol_vector_t *transAB; + /** vector of apol_infoflow_result_t from type B to A */ + apol_vector_t *transBA; + /** vector of apol_domain_trans_result_t from type A to B */ + apol_vector_t *domsAB; + /** vector of apol_domain_trans_result_t from type B to A */ + apol_vector_t *domsBA; +}; + +struct apol_types_relation_access +{ + const qpol_type_t *type; + /** vector of qpol_avrule_t pointers */ + apol_vector_t *rules; +}; + +/******************** actual analysis rountines ********************/ + +/** + * Find the attributes that both typeA and typeB have. Create a + * vector of those attributes (as represented as qpol_type_t pointers + * relative to the provided policy) and set r->attribs to that vector. + * + * @param p Policy containing types' information. + * @param typeA First type to check. + * @param typeB Other type to check. + * @param r Result structure to fill. + * + * @return 0 on success, < 0 on error. + */ +static int apol_types_relation_common_attribs(const apol_policy_t * p, + const qpol_type_t * typeA, const qpol_type_t * typeB, + apol_types_relation_result_t * r) +{ + qpol_iterator_t *iA = NULL, *iB = NULL; + apol_vector_t *vA = NULL, *vB = NULL; + int retval = -1; + + if (qpol_type_get_attr_iter(p->p, typeA, &iA) < 0 || qpol_type_get_attr_iter(p->p, typeB, &iB) < 0) { + goto cleanup; + } + if ((vA = apol_vector_create_from_iter(iA, NULL)) == NULL || + (vB = apol_vector_create_from_iter(iB, NULL)) == NULL || + (r->attribs = apol_vector_create_from_intersection(vA, vB, NULL, NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&iA); + qpol_iterator_destroy(&iB); + apol_vector_destroy(&vA); + apol_vector_destroy(&vB); + return retval; +} + +/** + * Find the roles whose allowed types include both typeA and typeB. + * Create a vector of those roles (as represented as qpol_role_t + * pointers relative to the provided policy) and set r->roles to that + * vector. + * + * @param p Policy containing types' information. + * @param typeA First type to check. + * @param typeB Other type to check. + * @param r Result structure to fill. + * + * @return 0 on success, < 0 on error. + */ +static int apol_types_relation_common_roles(const apol_policy_t * p, + const qpol_type_t * typeA, const qpol_type_t * typeB, apol_types_relation_result_t * r) +{ + const char *nameA, *nameB; + apol_role_query_t *rq = NULL; + apol_vector_t *vA = NULL, *vB = NULL; + int retval = -1; + + if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) { + goto cleanup; + } + if ((rq = apol_role_query_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_role_query_set_type(p, rq, nameA) < 0 || + apol_role_get_by_query(p, rq, &vA) < 0 || + apol_role_query_set_type(p, rq, nameB) < 0 || apol_role_get_by_query(p, rq, &vB) < 0) { + goto cleanup; + } + if ((r->roles = apol_vector_create_from_intersection(vA, vB, NULL, NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + } + + retval = 0; + cleanup: + apol_role_query_destroy(&rq); + apol_vector_destroy(&vA); + apol_vector_destroy(&vB); + return retval; +} + +/** + * Find the users whose roles have as their allowed types both typeA + * and typeB. Create a vector of those users (as represented as + * qpol_user_t pointers relative to the provided policy) and set + * r->users to that vector. + * + * @param p Policy containing types' information. + * @param typeA First type to check. + * @param typeB Other type to check. + * @param r Result structure to fill. + * + * @return 0 on success, < 0 on error. + */ +static int apol_types_relation_common_users(const apol_policy_t * p, + const qpol_type_t * typeA, const qpol_type_t * typeB, apol_types_relation_result_t * r) +{ + const char *nameA, *nameB; + apol_role_query_t *rq = NULL; + apol_vector_t *vA = NULL, *vB = NULL; + qpol_iterator_t *iter = NULL, *riter = NULL; + int retval = -1; + + if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) { + goto cleanup; + } + if ((rq = apol_role_query_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_role_query_set_type(p, rq, nameA) < 0 || + apol_role_get_by_query(p, rq, &vA) < 0 || + apol_role_query_set_type(p, rq, nameB) < 0 || apol_role_get_by_query(p, rq, &vB) < 0) { + goto cleanup; + } + + if ((r->users = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + if (qpol_policy_get_user_iter(p->p, &iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_user_t *user; + size_t i; + int inA = 0, inB = 0; + if (qpol_iterator_get_item(iter, (void **)&user) < 0) { + goto cleanup; + } + if (qpol_user_get_role_iter(p->p, user, &riter) < 0) { + goto cleanup; + } + for (; (!inA || !inB) && !qpol_iterator_end(riter); qpol_iterator_next(riter)) { + qpol_role_t *role; + if (qpol_iterator_get_item(riter, (void **)&role) < 0) { + goto cleanup; + } + if (!inA && apol_vector_get_index(vA, role, NULL, NULL, &i) == 0) { + inA = 1; + } + if (!inB && apol_vector_get_index(vB, role, NULL, NULL, &i) == 0) { + inB = 1; + } + } + qpol_iterator_destroy(&riter); + if (inA && inB && apol_vector_append(r->users, user) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + apol_role_query_destroy(&rq); + apol_vector_destroy(&vA); + apol_vector_destroy(&vB); + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&riter); + return retval; +} + +/** + * Comparison function for a vector of apol_types_relation_access_t + * pointers. Returns 0 if the access type at a matches the type b. + * + * @param a Pointer to an existing apol_types_relation_access_t. + * @param b Pointer to a qpol_type_t. + * @param data Unused. + * + * @return 0 if a's type matchs b, non-zero if not. + */ +static int apol_types_relation_access_compfunc(const void *a, const void *b, void *data __attribute__ ((unused))) +{ + apol_types_relation_access_t *access = (apol_types_relation_access_t *) a; + qpol_type_t *t = (qpol_type_t *) b; + return (int)((char *)access->type - (char *)t); +} + +/** + * Comparison function for a vector of apol_types_relation_access_t + * pointers. Returns 0 if the access type a matches the access type b. + * + * @param a Pointer to an existing apol_types_relation_access_t. + * @param b Pointer to another existing apol_types_relation_access_t. + * @param data Unused. + * + * @return 0 if a's type matchs b, non-zero if not. + */ +static int apol_types_relation_access_compfunc2(const void *a, const void *b, void *data __attribute__ ((unused))) +{ + apol_types_relation_access_t *accessA = (apol_types_relation_access_t *) a; + apol_types_relation_access_t *accessB = (apol_types_relation_access_t *) b; + return (int)((char *)accessA->type - (char *)accessB->type); +} + +/** + * Deallocate all space associated with a types relation access node, + * including the pointer itself. Does nothing if the pointer is + * alread NULL. + * + * @param data Pointer to an access node to free. + */ +static void apol_types_relation_access_free(void *data) +{ + apol_types_relation_access_t *a = (apol_types_relation_access_t *) data; + if (a != NULL) { + apol_vector_destroy(&a->rules); + free(a); + } +} + +/** + * Adds a rule to a vector of apol_types_relation_access_t pointers. + * Expands the rule's target type, appending new entries as necessary. + * + * @param p Policy from which rule originated. + * @param r Rule to expand and append. + * @param access Vector of apol_types_relation_access_t. + * + * @return 0 on success, < 0 on error. + */ +static int apol_types_relation_access_append_rule(const apol_policy_t * p, const qpol_avrule_t * r, apol_vector_t * access) +{ + const qpol_type_t *t; + apol_vector_t *expanded = NULL; + size_t i, j; + apol_types_relation_access_t *a; + int retval = -1; + if (qpol_avrule_get_target_type(p->p, r, &t) < 0 || (expanded = apol_query_expand_type(p, t)) == NULL) { + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(expanded); i++) { + t = apol_vector_get_element(expanded, i); + if (apol_vector_get_index(access, t, apol_types_relation_access_compfunc, NULL, &j) == 0) { + a = (apol_types_relation_access_t *) apol_vector_get_element(access, j); + } else { + if ((a = calloc(1, sizeof(*a))) == NULL || + (a->rules = apol_vector_create(NULL)) == NULL || apol_vector_append(access, a) < 0) { + ERR(p, "%s", strerror(errno)); + apol_types_relation_access_free(a); + goto cleanup; + } + a->type = t; + } + if (apol_vector_append(a->rules, (void *)r) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + retval = 0; + cleanup: + apol_vector_destroy(&expanded); + return retval; +} + +/** + * The following builds separate databases to hold rules for typeA and + * typeB respectively. The database holds a vector of pointers to + * apol_types_relation_access_t objects. Then compare access lists + * for typeA and typeB, determine common and unique access and have + * easy access to the relevant rules. + * + * @param p Policy to look up av rules. + * @param typeA First type to build access list. + * @param typeB Other type to build access list. + * @param accessesA Vector of apol_types_relation_access_t for typeA. + * @param accessesB Vector of apol_types_relation_access_t for typeB. + * + * @return 0 on success, < 0 on error. + */ +static int apol_types_relation_create_access_pools(const apol_policy_t * p, + const qpol_type_t * typeA, + const qpol_type_t * typeB, apol_vector_t * accessesA, apol_vector_t * accessesB) +{ + const char *nameA, *nameB; + apol_avrule_query_t *aq = NULL; + apol_vector_t *vA = NULL, *vB = NULL; + size_t i; + int retval = -1; + + if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) { + goto cleanup; + } + if ((aq = apol_avrule_query_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_avrule_query_set_rules(p, aq, QPOL_RULE_ALLOW) < 0 || + apol_avrule_query_set_source(p, aq, nameA, 1) < 0 || + apol_avrule_get_by_query(p, aq, &vA) < 0 || + apol_avrule_query_set_source(p, aq, nameB, 1) < 0 || apol_avrule_get_by_query(p, aq, &vB) < 0) { + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(vA); i++) { + qpol_avrule_t *r = (qpol_avrule_t *) apol_vector_get_element(vA, i); + if (apol_types_relation_access_append_rule(p, r, accessesA) < 0) { + goto cleanup; + } + } + for (i = 0; i < apol_vector_get_size(vB); i++) { + qpol_avrule_t *r = (qpol_avrule_t *) apol_vector_get_element(vB, i); + if (apol_types_relation_access_append_rule(p, r, accessesB) < 0) { + goto cleanup; + } + } + + retval = 0; + cleanup: + apol_avrule_query_destroy(&aq); + apol_vector_destroy(&vA); + apol_vector_destroy(&vB); + return retval; +} + +/** + * Allocate a new apol_types_relation_access_t and append it to a + * vector. The new access node's type will be set to a's type. The + * rules will be a clone of a's rules. + * + * @param p Policy from which rule originated. + * @param a Access node to duplicate. + * @param access Vector of apol_types_relation_access_t to append. + * + * @return 0 on success, < 0 on error. + */ +static int apol_types_relation_access_append(const apol_policy_t * p, const apol_types_relation_access_t * a, + apol_vector_t * access) +{ + apol_types_relation_access_t *new_a; + int retval = -1; + if ((new_a = calloc(1, sizeof(*new_a))) == NULL + || (new_a->rules = apol_vector_create_from_vector(a->rules, NULL, NULL, NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + new_a->type = a->type; + if (apol_vector_append(access, new_a) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + retval = 0; + cleanup: + if (retval != 0) { + apol_types_relation_access_free(new_a); + } + return retval; +} + +/** + * Find accesses, both similar and dissimilar, between both typeA and + * typeB. + * + * @param p Policy containing types' information. + * @param typeA First type to check. + * @param typeB Other type to check. + * @param do_similar 1 if to calculate similar accesses, 0 to skip. + * @param do_dissimilar 1 if to calculate dissimilar accesses, 0 to skip. + * @param r Result structure to fill. + * + * @return 0 on success, < 0 on error. + */ +static int apol_types_relation_accesses(const apol_policy_t * p, + const qpol_type_t * typeA, + const qpol_type_t * typeB, int do_similar, int do_dissimilar, + apol_types_relation_result_t * r) +{ + apol_vector_t *accessesA = NULL, *accessesB = NULL; + apol_types_relation_access_t *a, *b; + size_t i, j; + int retval = -1; + + if ((accessesA = apol_vector_create(apol_types_relation_access_free)) == NULL + || (accessesB = apol_vector_create(apol_types_relation_access_free)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + if (apol_types_relation_create_access_pools(p, typeA, typeB, accessesA, accessesB) < 0) { + goto cleanup; + } + apol_vector_sort(accessesA, apol_types_relation_access_compfunc2, NULL); + apol_vector_sort(accessesB, apol_types_relation_access_compfunc2, NULL); + + if (do_similar) { + if ((r->simA = apol_vector_create(apol_types_relation_access_free)) == NULL + || (r->simB = apol_vector_create(apol_types_relation_access_free)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + } + if (do_dissimilar) { + if ((r->disA = apol_vector_create(apol_types_relation_access_free)) == NULL + || (r->disB = apol_vector_create(apol_types_relation_access_free)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + } + + /* Step through each element for each access sorted list. If + * their types match and if do_similiar, then append the union + * of the access rules to the results. If their types do not + * match and if do_similar then add to results. + */ + for (i = j = 0; i < apol_vector_get_size(accessesA) && j < apol_vector_get_size(accessesB);) { + a = (apol_types_relation_access_t *) apol_vector_get_element(accessesA, i); + b = (apol_types_relation_access_t *) apol_vector_get_element(accessesB, j); + if (a->type == b->type) { + if (do_similar && + (apol_types_relation_access_append(p, a, r->simA) < 0 || + apol_types_relation_access_append(p, b, r->simB) < 0)) { + goto cleanup; + } + i++; + j++; + } else { + if (a->type < b->type) { + if (do_dissimilar && apol_types_relation_access_append(p, a, r->disA) < 0) { + goto cleanup; + } + i++; + } else { + if (do_dissimilar && apol_types_relation_access_append(p, b, r->disB) < 0) { + goto cleanup; + } + j++; + } + } + } + for (; do_dissimilar && i < apol_vector_get_size(accessesA); i++) { + a = (apol_types_relation_access_t *) apol_vector_get_element(accessesA, i); + if (apol_types_relation_access_append(p, a, r->disA) < 0) { + goto cleanup; + } + } + for (; do_dissimilar && j < apol_vector_get_size(accessesB); j++) { + b = (apol_types_relation_access_t *) apol_vector_get_element(accessesB, j); + if (apol_types_relation_access_append(p, b, r->disB) < 0) { + goto cleanup; + } + } + + retval = 0; + cleanup: + apol_vector_destroy(&accessesA); + apol_vector_destroy(&accessesB); + return retval; +} + +/** + * Find all allow rules that involve both types. Create a vector of + * those rules (as represented as qpol_avrule_t pointers relative to + * the provided policy) and set r->allows to that vector. + * + * @param p Policy containing types' information. + * @param typeA First type to check. + * @param typeB Other type to check. + * @param r Result structure to fill. + * + * @return 0 on success, < 0 on error. + */ +static int apol_types_relation_allows(const apol_policy_t * p, const qpol_type_t * typeA, const qpol_type_t * typeB, + apol_types_relation_result_t * r) +{ + const char *nameA, *nameB; + apol_avrule_query_t *aq = NULL; + apol_vector_t *v = NULL; + int retval = -1; + + if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) { + goto cleanup; + } + if ((aq = apol_avrule_query_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_avrule_query_set_rules(p, aq, QPOL_RULE_ALLOW) < 0 || + apol_avrule_query_set_source(p, aq, nameA, 1) < 0 || + apol_avrule_query_set_target(p, aq, nameB, 1) < 0 || apol_avrule_get_by_query(p, aq, &r->allows) < 0) { + goto cleanup; + } + if (apol_avrule_query_set_source(p, aq, nameB, 1) < 0 || + apol_avrule_query_set_target(p, aq, nameA, 1) < 0 || apol_avrule_get_by_query(p, aq, &v) < 0) { + goto cleanup; + } + if (apol_vector_cat(r->allows, v) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + retval = 0; + cleanup: + apol_avrule_query_destroy(&aq); + apol_vector_destroy(&v); + return retval; +} + +/** + * Find all type transition / type change rules that involve both + * types. Create a vector of those rules (as represented as + * qpol_terule_t pointers relative to the provided policy) and set + * r->types to that vector. + * + * @param p Policy containing types' information. + * @param typeA First type to check. + * @param typeB Other type to check. + * @param r Result structure to fill. + * + * @return 0 on success, < 0 on error. + */ +static int apol_types_relation_types(const apol_policy_t * p, const qpol_type_t * typeA, const qpol_type_t * typeB, + apol_types_relation_result_t * r) +{ + const char *nameA, *nameB; + apol_terule_query_t *tq = NULL; + apol_vector_t *v = NULL, *candidate_types = NULL; + const qpol_terule_t *rule; + const qpol_type_t *target, *default_type; + size_t i, j; + int retval = -1; + + if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) { + goto cleanup; + } + if ((r->types = apol_vector_create(NULL)) == NULL || (tq = apol_terule_query_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_terule_query_set_rules(p, tq, QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE) < 0 || + apol_terule_query_set_source(p, tq, nameA, 1) < 0 || + apol_terule_get_by_query(p, tq, &v) < 0 || + (candidate_types = apol_query_create_candidate_type_list(p, nameB, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) { + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(v); i++) { + rule = (qpol_terule_t *) apol_vector_get_element(v, i); + if (qpol_terule_get_target_type(p->p, rule, &target) < 0 || + qpol_terule_get_default_type(p->p, rule, &default_type) < 0) { + goto cleanup; + } + if ((apol_vector_get_index(candidate_types, target, NULL, NULL, &j) == 0 || + apol_vector_get_index(candidate_types, default_type, NULL, NULL, &j) == 0) && + apol_vector_append(r->types, (void *)rule) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + apol_vector_destroy(&v); + apol_vector_destroy(&candidate_types); + if (apol_terule_query_set_source(p, tq, nameB, 1) < 0 || + apol_terule_get_by_query(p, tq, &v) < 0 || + (candidate_types = apol_query_create_candidate_type_list(p, nameA, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) { + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(v); i++) { + rule = (qpol_terule_t *) apol_vector_get_element(v, i); + if (qpol_terule_get_target_type(p->p, rule, &target) < 0 || + qpol_terule_get_default_type(p->p, rule, &default_type) < 0) { + goto cleanup; + } + if ((apol_vector_get_index(candidate_types, target, NULL, NULL, &j) == 0 || + apol_vector_get_index(candidate_types, default_type, NULL, NULL, &j) == 0) && + apol_vector_append(r->types, (void *)rule) < 0) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + + retval = 0; + cleanup: + apol_terule_query_destroy(&tq); + apol_vector_destroy(&v); + apol_vector_destroy(&candidate_types); + return retval; +} + +/** + * Given a vector of apol_infoflow_result_t objects, deep copy to the + * results vector those infoflow results whose target type matches + * target_name (or any of target_name's attributes or aliases). + * + * @param p Policy within which to lookup types. + * @param v Vector of existing apol_infoflow_result_t. + * @param target_name Target type name. + * @param results Vector to which clone matching infoflow results. + * + * @return 0 on success, < 0 on error. + */ +static int apol_types_relation_clone_infoflow(const apol_policy_t * p, const apol_vector_t * v, const char *target_name, + apol_vector_t * results) +{ + apol_vector_t *candidate_types = NULL; + const qpol_type_t *target; + apol_infoflow_result_t *res, *new_res; + size_t i, j; + int retval = -1; + if ((candidate_types = apol_query_create_candidate_type_list(p, target_name, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) { + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(v); i++) { + res = (apol_infoflow_result_t *) apol_vector_get_element(v, i); + target = apol_infoflow_result_get_end_type(res); + if (apol_vector_get_index(candidate_types, target, NULL, NULL, &j) == 0) { + if ((new_res = infoflow_result_create_from_infoflow_result(res)) == NULL || + apol_vector_append(results, new_res) < 0) { + infoflow_result_free(new_res); + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + } + retval = 0; + cleanup: + apol_vector_destroy(&candidate_types); + return retval; +} + +/** + * Find all direct information flows between the two types. Create a + * vector of apol_infoflow_result_t and set r->dirflows to that vector. + * + * @param p Policy containing types' information. + * @param typeA First type to check. + * @param typeB Other type to check. + * @param r Result structure to fill. + * + * @return 0 on success, < 0 on error. + */ +static int apol_types_relation_directflow(const apol_policy_t * p, + const qpol_type_t * typeA, const qpol_type_t * typeB, apol_types_relation_result_t * r) +{ + const char *nameA, *nameB; + apol_infoflow_analysis_t *ia = NULL; + apol_vector_t *v = NULL; + apol_infoflow_graph_t *g = NULL; + int retval = -1; + + if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) { + goto cleanup; + } + if ((r->dirflows = apol_vector_create(infoflow_result_free)) == NULL || (ia = apol_infoflow_analysis_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_infoflow_analysis_set_mode(p, ia, APOL_INFOFLOW_MODE_DIRECT) < 0 || + apol_infoflow_analysis_set_dir(p, ia, APOL_INFOFLOW_EITHER) < 0 || + apol_infoflow_analysis_set_type(p, ia, nameA) < 0 || apol_infoflow_analysis_do(p, ia, &v, &g) < 0) { + goto cleanup; + } + if (apol_types_relation_clone_infoflow(p, v, nameB, r->dirflows) < 0) { + goto cleanup; + } + + retval = 0; + cleanup: + apol_vector_destroy(&v); + apol_infoflow_analysis_destroy(&ia); + apol_infoflow_graph_destroy(&g); + return retval; +} + +/** + * Find (some) transitive information flows between the two types. + * + * @param p Policy containing types' information. + * @param typeA First type to check. + * @param typeB Other type to check. + * @param do_transAB 1 if to find paths from type A to B, 0 to skip. + * @param do_transBA 1 if to find paths from type B to A, 0 to skip. + * @param r Result structure to fill. + * + * @return 0 on success, < 0 on error. + */ +static int apol_types_relation_transflow(const apol_policy_t * p, + const qpol_type_t * typeA, + const qpol_type_t * typeB, + unsigned int do_transAB, unsigned int do_transBA, apol_types_relation_result_t * r) +{ + const char *nameA, *nameB; + apol_infoflow_analysis_t *ia = NULL; + apol_vector_t *v = NULL; + apol_infoflow_graph_t *g = NULL; + int retval = -1; + + if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) { + goto cleanup; + } + if ((ia = apol_infoflow_analysis_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_infoflow_analysis_set_mode(p, ia, APOL_INFOFLOW_MODE_TRANS) < 0 || + apol_infoflow_analysis_set_dir(p, ia, APOL_INFOFLOW_OUT) < 0) { + goto cleanup; + } + if (do_transAB) { + if (apol_infoflow_analysis_set_type(p, ia, nameA) < 0 || apol_infoflow_analysis_do(p, ia, &v, &g) < 0) { + goto cleanup; + } + if ((r->transAB = apol_vector_create(infoflow_result_free)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + if (apol_types_relation_clone_infoflow(p, v, nameB, r->transAB) < 0) { + goto cleanup; + } + } + if (do_transBA) { + apol_vector_destroy(&v); + if ((do_transAB && + apol_infoflow_analysis_do_more(p, g, nameB, &v) < 0) || + (!do_transAB && + (apol_infoflow_analysis_set_type(p, ia, nameB) < 0 || apol_infoflow_analysis_do(p, ia, &v, &g) < 0))) { + goto cleanup; + } + if ((r->transBA = apol_vector_create(infoflow_result_free)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + if (apol_types_relation_clone_infoflow(p, v, nameA, r->transBA) < 0) { + goto cleanup; + } + } + retval = 0; + cleanup: + apol_vector_destroy(&v); + apol_infoflow_analysis_destroy(&ia); + apol_infoflow_graph_destroy(&g); + return retval; +} + +/** + * Given a vector of apol_domain_trans_result_t objects, deep copy to + * the results vector those domain transition results whose target + * type matches target_name (or any of target_name's attributes or + * aliases). + * + * @param p Policy within which to lookup types. + * @param v Vector of existing apol_domain_trans_result_t. + * @param target_name Target type name. + * @param results Vector to which clone matching domain transition + * results. + * + * @return 0 on success, < 0 on error. + */ +static int apol_types_relation_clone_domaintrans(const apol_policy_t * p, const apol_vector_t * v, const char *target_name, + apol_vector_t * results) +{ + apol_vector_t *candidate_types = NULL; + const qpol_type_t *target; + apol_domain_trans_result_t *res, *new_res; + size_t i, j; + int retval = -1; + if ((candidate_types = apol_query_create_candidate_type_list(p, target_name, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) { + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(v); i++) { + res = (apol_domain_trans_result_t *) apol_vector_get_element(v, i); + target = apol_domain_trans_result_get_end_type(res); + if (apol_vector_get_index(candidate_types, target, NULL, NULL, &j) == 0) { + if ((new_res = apol_domain_trans_result_create_from_domain_trans_result(res)) == NULL || + apol_vector_append(results, new_res) < 0) { + domain_trans_result_free(new_res); + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + } + retval = 0; + cleanup: + apol_vector_destroy(&candidate_types); + return retval; +} + +/** + * Find domain transitions between the two types. + * + * @param p Policy containing types' information. + * @param typeA First type to check. + * @param typeB Other type to check. + * @param do_domainAB 1 if to find transitions from type A to B, 0 to skip. + * @param do_domainBA 1 if to find transitions from type B to A, 0 to skip. + * @param r Result structure to fill. + * + * @return 0 on success, < 0 on error. + */ +static int apol_types_relation_domain(apol_policy_t * p, + const qpol_type_t * typeA, + const qpol_type_t * typeB, + unsigned int do_domainsAB, unsigned int do_domainsBA, apol_types_relation_result_t * r) +{ + const char *nameA, *nameB; + apol_domain_trans_analysis_t *dta = NULL; + apol_vector_t *v = NULL; + int retval = -1; + + if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) { + goto cleanup; + } + if ((dta = apol_domain_trans_analysis_create()) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_policy_build_domain_trans_table(p) < 0 || + apol_domain_trans_analysis_set_direction(p, dta, APOL_DOMAIN_TRANS_DIRECTION_FORWARD) < 0) { + goto cleanup; + } + if (do_domainsAB) { + apol_policy_reset_domain_trans_table(p); + if (apol_domain_trans_analysis_set_start_type(p, dta, nameA) < 0 || apol_domain_trans_analysis_do(p, dta, &v) < 0) { + goto cleanup; + } + if ((r->domsAB = apol_vector_create(domain_trans_result_free)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + if (apol_types_relation_clone_domaintrans(p, v, nameB, r->domsAB) < 0) { + goto cleanup; + } + } + if (do_domainsBA) { + apol_vector_destroy(&v); + apol_policy_reset_domain_trans_table(p); + if (apol_domain_trans_analysis_set_start_type(p, dta, nameB) < 0 || apol_domain_trans_analysis_do(p, dta, &v) < 0) { + goto cleanup; + } + if ((r->domsBA = apol_vector_create(domain_trans_result_free)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + if (apol_types_relation_clone_domaintrans(p, v, nameA, r->domsBA) < 0) { + goto cleanup; + } + } + retval = 0; + cleanup: + apol_vector_destroy(&v); + apol_domain_trans_analysis_destroy(&dta); + return retval; +} + +/******************** public functions below ********************/ + +int apol_types_relation_analysis_do(apol_policy_t * p, const apol_types_relation_analysis_t * tr, apol_types_relation_result_t ** r) +{ + const qpol_type_t *typeA, *typeB; + unsigned char isattrA, isattrB; + unsigned int do_similar_access, do_dissimilar_access; + unsigned int do_transAB, do_transBA; + unsigned int do_domainAB, do_domainBA; + int retval = -1; + *r = NULL; + + if (tr->typeA == NULL || tr->typeB == NULL) { + ERR(p, "%s", strerror(EINVAL)); + goto cleanup; + } + if (apol_query_get_type(p, tr->typeA, &typeA) < 0 || + apol_query_get_type(p, tr->typeB, &typeB) < 0 || + qpol_type_get_isattr(p->p, typeA, &isattrA) < 0 || qpol_type_get_isattr(p->p, typeB, &isattrB) < 0) { + goto cleanup; + } + if (isattrA) { + ERR(p, "Symbol %s is an attribute.", tr->typeA); + goto cleanup; + } + if (isattrB) { + ERR(p, "Symbol %s is an attribute.", tr->typeB); + goto cleanup; + } + if ((*r = calloc(1, sizeof(**r))) == NULL) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + if ((tr->analyses & APOL_TYPES_RELATION_COMMON_ATTRIBS) && apol_types_relation_common_attribs(p, typeA, typeB, *r) < 0) { + goto cleanup; + } + if ((tr->analyses & APOL_TYPES_RELATION_COMMON_ROLES) && apol_types_relation_common_roles(p, typeA, typeB, *r) < 0) { + goto cleanup; + } + if ((tr->analyses & APOL_TYPES_RELATION_COMMON_USERS) && apol_types_relation_common_users(p, typeA, typeB, *r) < 0) { + goto cleanup; + } + do_similar_access = tr->analyses & APOL_TYPES_RELATION_SIMILAR_ACCESS; + do_dissimilar_access = tr->analyses & APOL_TYPES_RELATION_DISSIMILAR_ACCESS; + if ((do_similar_access || do_dissimilar_access) && + apol_types_relation_accesses(p, typeA, typeB, do_similar_access, do_dissimilar_access, *r) < 0) { + goto cleanup; + } + if ((tr->analyses & APOL_TYPES_RELATION_ALLOW_RULES) && apol_types_relation_allows(p, typeA, typeB, *r) < 0) { + goto cleanup; + } + if ((tr->analyses & APOL_TYPES_RELATION_TYPE_RULES) && apol_types_relation_types(p, typeA, typeB, *r) < 0) { + goto cleanup; + } + if ((tr->analyses & APOL_TYPES_RELATION_DIRECT_FLOW) && apol_types_relation_directflow(p, typeA, typeB, *r) < 0) { + goto cleanup; + } + do_transAB = tr->analyses & APOL_TYPES_RELATION_TRANS_FLOW_AB; + do_transBA = tr->analyses & APOL_TYPES_RELATION_TRANS_FLOW_BA; + if ((do_transAB || do_transBA) && apol_types_relation_transflow(p, typeA, typeB, do_transAB, do_transBA, *r) < 0) { + goto cleanup; + } + do_domainAB = tr->analyses & APOL_TYPES_RELATION_DOMAIN_TRANS_AB; + do_domainBA = tr->analyses & APOL_TYPES_RELATION_DOMAIN_TRANS_BA; + if ((do_domainAB || do_domainBA) && apol_types_relation_domain(p, typeA, typeB, do_domainAB, do_domainBA, *r) < 0) { + goto cleanup; + } + + retval = 0; + cleanup: + if (retval != 0) { + apol_types_relation_result_destroy(r); + } + return retval; +} + +apol_types_relation_analysis_t *apol_types_relation_analysis_create(void) +{ + return calloc(1, sizeof(apol_types_relation_analysis_t)); +} + +void apol_types_relation_analysis_destroy(apol_types_relation_analysis_t ** tr) +{ + if (*tr != NULL) { + free((*tr)->typeA); + free((*tr)->typeB); + free(*tr); + *tr = NULL; + } +} + +int apol_types_relation_analysis_set_first_type(const apol_policy_t * p, apol_types_relation_analysis_t * tr, const char *name) +{ + if (name == NULL) { + ERR(p, "%s", strerror(EINVAL)); + return -1; + } + return apol_query_set(p, &tr->typeA, NULL, name); +} + +int apol_types_relation_analysis_set_other_type(const apol_policy_t * p, apol_types_relation_analysis_t * tr, const char *name) +{ + if (name == NULL) { + ERR(p, "%s", strerror(EINVAL)); + return -1; + } + return apol_query_set(p, &tr->typeB, NULL, name); +} + +int apol_types_relation_analysis_set_analyses(const apol_policy_t * p __attribute__ ((unused)), + apol_types_relation_analysis_t * tr, unsigned int analyses) +{ + if (analyses != 0) { + tr->analyses = analyses; + } else { + tr->analyses = ~0U; + } + return 0; +} + +/*************** functions to access type relation results ***************/ + +void apol_types_relation_result_destroy(apol_types_relation_result_t ** result) +{ + if (*result != NULL) { + apol_vector_destroy(&(*result)->attribs); + apol_vector_destroy(&(*result)->roles); + apol_vector_destroy(&(*result)->users); + apol_vector_destroy(&(*result)->simA); + apol_vector_destroy(&(*result)->simB); + apol_vector_destroy(&(*result)->disA); + apol_vector_destroy(&(*result)->disB); + apol_vector_destroy(&(*result)->allows); + apol_vector_destroy(&(*result)->types); + apol_vector_destroy(&(*result)->dirflows); + apol_vector_destroy(&(*result)->transAB); + apol_vector_destroy(&(*result)->transBA); + apol_vector_destroy(&(*result)->domsAB); + apol_vector_destroy(&(*result)->domsBA); + free(*result); + *result = NULL; + } +} + +const apol_vector_t *apol_types_relation_result_get_attributes(const apol_types_relation_result_t * result) +{ + return result->attribs; +} + +const apol_vector_t *apol_types_relation_result_get_roles(const apol_types_relation_result_t * result) +{ + return result->roles; +} + +const apol_vector_t *apol_types_relation_result_get_users(const apol_types_relation_result_t * result) +{ + return result->users; +} + +const apol_vector_t *apol_types_relation_result_get_similar_first(const apol_types_relation_result_t * result) +{ + return result->simA; +} + +const apol_vector_t *apol_types_relation_result_get_similar_other(const apol_types_relation_result_t * result) +{ + return result->simB; +} + +const apol_vector_t *apol_types_relation_result_get_dissimilar_first(const apol_types_relation_result_t * result) +{ + return result->disA; +} + +const apol_vector_t *apol_types_relation_result_get_dissimilar_other(const apol_types_relation_result_t * result) +{ + return result->disB; +} + +const apol_vector_t *apol_types_relation_result_get_allowrules(const apol_types_relation_result_t * result) +{ + return result->allows; +} + +const apol_vector_t *apol_types_relation_result_get_typerules(const apol_types_relation_result_t * result) +{ + return result->types; +} + +const apol_vector_t *apol_types_relation_result_get_directflows(const apol_types_relation_result_t * result) +{ + return result->dirflows; +} + +const apol_vector_t *apol_types_relation_result_get_transflowsAB(const apol_types_relation_result_t * result) +{ + return result->transAB; +} + +const apol_vector_t *apol_types_relation_result_get_transflowsBA(const apol_types_relation_result_t * result) +{ + return result->transBA; +} + +const apol_vector_t *apol_types_relation_result_get_domainsAB(const apol_types_relation_result_t * result) +{ + return result->domsAB; +} + +const apol_vector_t *apol_types_relation_result_get_domainsBA(const apol_types_relation_result_t * result) +{ + return result->domsBA; +} + +const qpol_type_t *apol_types_relation_access_get_type(const apol_types_relation_access_t * a) +{ + return a->type; +} + +const apol_vector_t *apol_types_relation_access_get_rules(const apol_types_relation_access_t * a) +{ + return a->rules; +} diff --git a/libapol/src/user-query.c b/libapol/src/user-query.c new file mode 100644 index 0000000..066e005 --- /dev/null +++ b/libapol/src/user-query.c @@ -0,0 +1,198 @@ +/** + * @file + * + * Provides a way for setools to make queries about users within a + * policy. The caller obtains a query object, fills in its + * parameters, and then runs the query; it obtains a vector of + * results. Searches are conjunctive -- all fields of the search + * query must match for a datum to be added to the results query. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "policy-query-internal.h" + +#include <errno.h> + +struct apol_user_query +{ + char *user_name, *role_name; + apol_mls_level_t *default_level; + apol_mls_range_t *range; + unsigned int flags; + regex_t *user_regex, *role_regex; +}; + +/******************** user queries ********************/ + +int apol_user_get_by_query(const apol_policy_t * p, apol_user_query_t * u, apol_vector_t ** v) +{ + qpol_iterator_t *iter = NULL, *role_iter = NULL; + apol_mls_level_t *default_level = NULL; + apol_mls_range_t *range = NULL; + int retval = -1, append_user; + *v = NULL; + if (qpol_policy_get_user_iter(p->p, &iter) < 0) { + return -1; + } + if ((*v = apol_vector_create(NULL)) == NULL) { + ERR(p, "%s", strerror(errno)); + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + qpol_user_t *user; + if (qpol_iterator_get_item(iter, (void **)&user) < 0) { + goto cleanup; + } + append_user = 1; + if (u != NULL) { + const char *user_name; + int compval; + const qpol_mls_level_t *mls_default_level; + const qpol_mls_range_t *mls_range; + + qpol_iterator_destroy(&role_iter); + apol_mls_level_destroy(&default_level); + apol_mls_range_destroy(&range); + + if (qpol_user_get_name(p->p, user, &user_name) < 0) { + goto cleanup; + } + compval = apol_compare(p, user_name, u->user_name, u->flags, &(u->user_regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + if (qpol_user_get_role_iter(p->p, user, &role_iter) < 0) { + goto cleanup; + } + if (u->role_name != NULL && u->role_name[0] != '\0') { + append_user = 0; + for (; !qpol_iterator_end(role_iter); qpol_iterator_next(role_iter)) { + qpol_role_t *role; + const char *role_name; + if (qpol_iterator_get_item(role_iter, (void **)&role) < 0 || + qpol_role_get_name(p->p, role, &role_name) < 0) { + goto cleanup; + } + compval = apol_compare(p, role_name, u->role_name, u->flags, &(u->role_regex)); + if (compval < 0) { + goto cleanup; + } else if (compval == 1) { + append_user = 1; + break; + } + } + } + if (apol_policy_is_mls(p)) { + if (qpol_user_get_dfltlevel(p->p, user, &mls_default_level) < 0 || + (default_level = apol_mls_level_create_from_qpol_mls_level(p, mls_default_level)) == NULL) { + goto cleanup; + } + compval = apol_mls_level_compare(p, default_level, u->default_level); + apol_mls_level_destroy(&default_level); + if (compval < 0) { + goto cleanup; + } else if (compval != APOL_MLS_EQ) { + continue; + } + + if (qpol_user_get_range(p->p, user, &mls_range) < 0 || + (range = apol_mls_range_create_from_qpol_mls_range(p, mls_range)) == NULL) { + goto cleanup; + } + compval = apol_mls_range_compare(p, range, u->range, u->flags); + apol_mls_range_destroy(&range); + if (compval < 0) { + goto cleanup; + } else if (compval == 0) { + continue; + } + } + } + if (append_user && apol_vector_append(*v, user)) { + ERR(p, "%s", strerror(ENOMEM)); + goto cleanup; + } + } + retval = 0; + cleanup: + if (retval != 0) { + apol_vector_destroy(v); + } + qpol_iterator_destroy(&iter); + qpol_iterator_destroy(&role_iter); + apol_mls_level_destroy(&default_level); + apol_mls_range_destroy(&range); + return retval; +} + +apol_user_query_t *apol_user_query_create(void) +{ + return calloc(1, sizeof(apol_user_query_t)); +} + +void apol_user_query_destroy(apol_user_query_t ** u) +{ + if (*u != NULL) { + free((*u)->user_name); + free((*u)->role_name); + apol_mls_level_destroy(&((*u)->default_level)); + apol_mls_range_destroy(&((*u)->range)); + apol_regex_destroy(&(*u)->user_regex); + apol_regex_destroy(&(*u)->role_regex); + free(*u); + *u = NULL; + } +} + +int apol_user_query_set_user(const apol_policy_t * p, apol_user_query_t * u, const char *name) +{ + return apol_query_set(p, &u->user_name, &u->user_regex, name); +} + +int apol_user_query_set_role(const apol_policy_t * p, apol_user_query_t * u, const char *role) +{ + return apol_query_set(p, &u->role_name, &u->role_regex, role); +} + +int apol_user_query_set_default_level(const apol_policy_t * p + __attribute__ ((unused)), apol_user_query_t * u, apol_mls_level_t * level) +{ + u->default_level = level; + return 0; +} + +int apol_user_query_set_range(const apol_policy_t * p __attribute__ ((unused)), + apol_user_query_t * u, apol_mls_range_t * range, unsigned int range_match) +{ + if (u->range != NULL) { + apol_mls_range_destroy(&u->range); + } + u->range = range; + u->flags = (u->flags & ~APOL_QUERY_FLAGS) | range_match; + return 0; +} + +int apol_user_query_set_regex(const apol_policy_t * p, apol_user_query_t * u, int is_regex) +{ + return apol_query_set_regex(p, &u->flags, is_regex); +} diff --git a/libapol/src/util.c b/libapol/src/util.c new file mode 100644 index 0000000..dd6d300 --- /dev/null +++ b/libapol/src/util.c @@ -0,0 +1,659 @@ +/** + * @file + * + * Implementation of utility functions. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2001-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <apol/util.h> + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdbool.h> + +/* these are needed for nodecons and IPv4 and IPv6 */ +#include <qpol/nodecon_query.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> /* needed for portcon's protocol */ + +/* use 8k line size */ +#define APOL_LINE_SZ 8192 +#define APOL_ENVIRON_VAR_NAME "APOL_INSTALL_DIR" + +const char *libapol_get_version(void) +{ + return LIBAPOL_VERSION_STRING; +} + +int apol_str_to_internal_ip(const char *str, uint32_t ip[4]) +{ + bool ipv4 = false; + bool ipv6 = false; + + if (!str || !ip) { + errno = EINVAL; + return -1; + } + + ip[0] = ip[1] = ip[2] = ip[3] = 0; + + if (strchr(str, '.')) + ipv4 = true; + + if (strchr(str, ':')) + ipv6 = true; + + if (ipv4 == ipv6) { + errno = EINVAL; + return -1; + } + + if (ipv4) { + unsigned char *p = (unsigned char *)&(ip[0]); + int seg = 0; + uint32_t val = 0; /* value of current segment of address */ + size_t len = strlen(str), i; + for (i = 0; i <= len; i++) { + if (str[i] == '.' || str[i] == '\0') { + if (val > 255) { + errno = EINVAL; + return -1; + } + + p[seg] = (unsigned char)(0xff & val); + seg++; + val = 0; + if (seg == 4) + break; + } else if (isdigit(str[i])) { + char tmp[2] = { str[i], 0 }; + val = val * 10 + atoi(tmp); + } else { + errno = EINVAL; + return -1; + } + } + } else { + struct in6_addr addr; + if (inet_pton(AF_INET6, str, &addr) <= 0) { + return -1; + } + memcpy(ip, addr.s6_addr32, 16); + } + + return ipv4 ? QPOL_IPV4 : QPOL_IPV6; +} + +const char *apol_objclass_to_str(uint32_t objclass) +{ + switch (objclass) { + case QPOL_CLASS_BLK_FILE: + return "block"; + case QPOL_CLASS_CHR_FILE: + return "char"; + case QPOL_CLASS_DIR: + return "dir"; + case QPOL_CLASS_FIFO_FILE: + return "fifo"; + case QPOL_CLASS_FILE: + return "file"; + case QPOL_CLASS_LNK_FILE: + return "link"; + case QPOL_CLASS_SOCK_FILE: + return "sock"; + case QPOL_CLASS_ALL: + return "any"; + } + return NULL; +} + +uint32_t apol_str_to_objclass(const char *objclass) +{ + if (objclass == NULL) { + errno = EINVAL; + return 0; + } + if (strcmp(objclass, "block") == 0) { + return QPOL_CLASS_BLK_FILE; + } + if (strcmp(objclass, "char") == 0) { + return QPOL_CLASS_CHR_FILE; + } + if (strcmp(objclass, "dir") == 0) { + return QPOL_CLASS_DIR; + } + if (strcmp(objclass, "fifo") == 0) { + return QPOL_CLASS_FIFO_FILE; + } + if (strcmp(objclass, "file") == 0) { + return QPOL_CLASS_FILE; + } + if (strcmp(objclass, "link") == 0) { + return QPOL_CLASS_LNK_FILE; + } + if (strcmp(objclass, "sock") == 0) { + return QPOL_CLASS_SOCK_FILE; + } + if (strcmp(objclass, "any") == 0) { + return QPOL_CLASS_ALL; + } + return 0; +} + +const char *apol_protocol_to_str(uint8_t protocol) +{ + switch (protocol) { + case IPPROTO_TCP: + return "tcp"; + case IPPROTO_UDP: + return "udp"; + default: + errno = EPROTONOSUPPORT; + return NULL; + } +} + +uint8_t apol_str_to_protocol(const char *protocol_str) +{ + if (protocol_str == NULL) { + errno = EINVAL; + return 0; + } + if (strcmp(protocol_str, "tcp") == 0 || strcmp(protocol_str, "TCP") == 0) { + return IPPROTO_TCP; + } + if (strcmp(protocol_str, "udp") == 0 || strcmp(protocol_str, "UDP") == 0) { + return IPPROTO_UDP; + } + errno = EPROTONOSUPPORT; + return 0; +} + +const char *apol_fs_use_behavior_to_str(uint32_t behavior) +{ + switch (behavior) { + case QPOL_FS_USE_XATTR: + return "fs_use_xattr"; + case QPOL_FS_USE_TASK: + return "fs_use_task"; + case QPOL_FS_USE_TRANS: + return "fs_use_trans"; + case QPOL_FS_USE_GENFS: + return "fs_use_genfs"; + case QPOL_FS_USE_NONE: + return "fs_use_none"; + case QPOL_FS_USE_PSID: + return "fs_use_psid"; + } + return NULL; +} + +int apol_str_to_fs_use_behavior(const char *behavior) +{ + if (strcmp(behavior, "fs_use_xattr") == 0) { + return QPOL_FS_USE_XATTR; + } else if (strcmp(behavior, "fs_use_task") == 0) { + return QPOL_FS_USE_TASK; + } else if (strcmp(behavior, "fs_use_trans") == 0) { + return QPOL_FS_USE_TRANS; + } else if (strcmp(behavior, "fs_use_genfs") == 0) { + return QPOL_FS_USE_GENFS; + } else if (strcmp(behavior, "fs_use_none") == 0) { + return QPOL_FS_USE_NONE; + } else if (strcmp(behavior, "fs_use_psid") == 0) { + return QPOL_FS_USE_PSID; + } + return -1; +} + +const char *apol_rule_type_to_str(uint32_t rule_type) +{ + switch (rule_type) { + case QPOL_RULE_ALLOW: + return "allow"; + case QPOL_RULE_NEVERALLOW: + return "neverallow"; + case QPOL_RULE_AUDITALLOW: + return "auditallow"; + case QPOL_RULE_DONTAUDIT: + return "dontaudit"; + case QPOL_RULE_TYPE_TRANS: + return "type_transition"; + case QPOL_RULE_TYPE_CHANGE: + return "type_change"; + case QPOL_RULE_TYPE_MEMBER: + return "type_member"; + } + return NULL; +} + +const char *apol_cond_expr_type_to_str(uint32_t expr_type) +{ + switch (expr_type) { + case QPOL_COND_EXPR_BOOL: + return ""; + case QPOL_COND_EXPR_NOT: + return "!"; + case QPOL_COND_EXPR_OR: + return "||"; + case QPOL_COND_EXPR_AND: + return "&&"; + case QPOL_COND_EXPR_XOR: + return "^"; + case QPOL_COND_EXPR_EQ: + return "=="; + case QPOL_COND_EXPR_NEQ: + return "!="; + } + return NULL; +} + +char *apol_file_find(const char *file_name) +{ + char *file = NULL, *var = NULL, *dirs[3]; + size_t i; + int rt; + + if (file_name == NULL) { + errno = EINVAL; + return NULL; + } + + /* check current directory, environment variable, and then + * installed directory */ + dirs[0] = "."; + dirs[1] = getenv(APOL_ENVIRON_VAR_NAME); + dirs[2] = APOL_INSTALL_DIR; + for (i = 0; i < 3; i++) { + if ((var = dirs[i]) != NULL) { + if (asprintf(&file, "%s/%s", var, file_name) < 0) { + return NULL; + } + rt = access(file, R_OK); + free(file); + if (rt == 0) { + return strdup(var); + } + } + } + + /* didn't find it */ + return NULL; +} + +char *apol_file_find_path(const char *file_name) +{ + char *file = NULL, *var = NULL, *dirs[3]; + size_t i; + int rt; + + if (file_name == NULL) { + errno = EINVAL; + return NULL; + } + + /* check current directory, environment variable, and then + * installed directory */ + dirs[0] = "."; + dirs[1] = getenv(APOL_ENVIRON_VAR_NAME); + dirs[2] = APOL_INSTALL_DIR; + for (i = 0; i < 3; i++) { + if ((var = dirs[i]) != NULL) { + if (asprintf(&file, "%s/%s", var, file_name) < 0) { + return NULL; + } + rt = access(file, R_OK); + if (rt == 0) { + return file; + } + free(file); + } + } + + /* didn't find it */ + return NULL; +} + +char *apol_file_find_user_config(const char *file_name) +{ + char *file, *var; + int rt; + + if (file_name == NULL) { + errno = EINVAL; + return NULL; + } + var = getenv("HOME"); + if (var) { + if (asprintf(&file, "%s/%s", var, file_name) < 0) { + return NULL; + } + rt = access(file, R_OK); + if (rt == 0) { + return file; + } else { + free(file); + return NULL; + } + } + return NULL; +} + +int apol_file_read_to_buffer(const char *fname, char **buf, size_t * len) +{ + FILE *file = NULL; + const size_t BUF_SIZE = 1024; + size_t size = 0, r; + char *bufp, *b; + + assert(*buf == NULL); + assert(len); + *len = 0; + while (1) { + size += BUF_SIZE; + r = 0; + b = (char *)realloc(*buf, size * sizeof(char)); + if (b == NULL) { + free(*buf); + *buf = NULL; + *len = 0; + if (file) + fclose(file); + return -1; + } + *buf = b; + if (!file) { + file = fopen(fname, "rb"); + if (!file) { + free(*buf); + *buf = NULL; + *len = 0; + return -1; + } + } + bufp = &((*buf)[size - BUF_SIZE]); + r = fread(bufp, sizeof(char), BUF_SIZE, file); + *len += r; + if (r < BUF_SIZE) { + if (feof(file)) { + fclose(file); + break; + } else { + free(*buf); + *buf = NULL; + *len = 0; + fclose(file); + return -1; + } + } + } + return 0; +} + +char *apol_config_get_var(const char *var, FILE * fp) +{ + char line[APOL_LINE_SZ], t1[APOL_LINE_SZ], t2[APOL_LINE_SZ]; + char *line_ptr = NULL; + + if (var == NULL || fp == NULL) { + errno = EINVAL; + return NULL; + } + + rewind(fp); + while (fgets(line, APOL_LINE_SZ, fp) != NULL) { + if ((line_ptr = strdup(line)) == NULL) { + return NULL; + } + apol_str_trim(line_ptr); + if (line_ptr[0] == '#' || sscanf(line_ptr, "%s %[^\n]", t1, t2) != 2 || strcasecmp(var, t1) != 0) { + free(line_ptr); + continue; + } else { + free(line_ptr); + return strdup(t2); + } + } + return NULL; +} + +apol_vector_t *apol_str_split(const char *s, const char *delim) +{ + char *orig_s = NULL, *dup_s = NULL, *v, *token; + apol_vector_t *list = NULL; + int error = 0; + + if (s == NULL || delim == NULL) { + error = EINVAL; + goto cleanup; + } + if ((list = apol_vector_create(free)) == NULL || (orig_s = strdup(s)) == NULL) { + error = errno; + goto cleanup; + } + v = orig_s; + while ((token = strsep(&v, delim)) != NULL) { + if (strcmp(token, "") != 0 && !apol_str_is_only_white_space(token)) { + if ((dup_s = strdup(token)) == NULL || apol_vector_append(list, dup_s) < 0) { + error = errno; + free(dup_s); + goto cleanup; + } + } + } + cleanup: + free(orig_s); + if (error != 0) { + apol_vector_destroy(&list); + errno = error; + return NULL; + } + return list; +} + +char *apol_str_join(const apol_vector_t * list, const char *delim) +{ + char *val, *s; + size_t i, len; + + if (list == NULL || delim == NULL) { + errno = EINVAL; + return NULL; + } + if (apol_vector_get_size(list) == 0) { + return strdup(""); + } + s = apol_vector_get_element(list, 0); + if ((val = strdup(s)) == NULL) { + return NULL; + } + len = strlen(val) + 1; + for (i = 1; i < apol_vector_get_size(list); i++) { + s = apol_vector_get_element(list, i); + if (apol_str_appendf(&val, &len, "%s%s", delim, s) < 0) { + return NULL; + } + } + return val; +} + +/** + * Given a string, if the string begins with whitespace then allocate + * a new string that does not contain those whitespaces. + * + * @param str String to modify. + */ +static void trim_leading_whitespace(char *str) +{ + size_t i, len; + for (i = 0; str[i] != '\0' && isspace(str[i]); i++) ; + len = strlen(str + i); + memmove(str, str + i, len + 1); +} + +/** + * Given a mutable string, replace trailing whitespace characters with + * null characters. + * + * @param str String to modify. + */ +static void trim_trailing_whitespace(char *str) +{ + size_t length; + length = strlen(str); + while (length > 0 && isspace(str[length - 1])) { + str[length - 1] = '\0'; + length--; + } +} + +void apol_str_trim(char *str) +{ + if (str == NULL) { + errno = EINVAL; + return; + } + trim_leading_whitespace(str); + trim_trailing_whitespace(str); +} + +int apol_str_append(char **tgt, size_t * tgt_sz, const char *str) +{ + size_t str_len; + if (str == NULL || (str_len = strlen(str)) == 0) + return 0; + if (tgt == NULL) { + errno = EINVAL; + return -1; + } + str_len++; + /* target is currently empty */ + if (*tgt == NULL || *tgt_sz == 0) { + *tgt = (char *)malloc(str_len); + if (*tgt == NULL) { + *tgt_sz = 0; + return -1; + } + *tgt_sz = str_len; + strcpy(*tgt, str); + return 0; + } else { + /* tgt has some memory */ + char *t = (char *)realloc(*tgt, *tgt_sz + str_len); + if (t == NULL) { + int error = errno; + free(*tgt); + *tgt = NULL; + *tgt_sz = 0; + errno = error; + return -1; + } + *tgt = t; + *tgt_sz += str_len; + strcat(*tgt, str); + return 0; + } +} + +int apol_str_appendf(char **tgt, size_t * tgt_sz, const char *fmt, ...) +{ + va_list ap; + int error; + if (fmt == NULL || strlen(fmt) == 0) + return 0; + if (tgt == NULL) { + errno = EINVAL; + return -1; + } + va_start(ap, fmt); + /* target is currently empty */ + if (*tgt == NULL || *tgt_sz == 0) { + if (vasprintf(tgt, fmt, ap) < 0) { + error = errno; + *tgt = NULL; + *tgt_sz = 0; + va_end(ap); + errno = error; + return -1; + } + *tgt_sz = strlen(*tgt) + 1; + va_end(ap); + return 0; + } else { + /* tgt has some memory */ + char *t, *u; + size_t str_len; + if (vasprintf(&t, fmt, ap) < 0) { + error = errno; + free(*tgt); + *tgt_sz = 0; + va_end(ap); + errno = error; + return -1; + } + va_end(ap); + str_len = strlen(t); + if ((u = (char *)realloc(*tgt, *tgt_sz + str_len)) == NULL) { + error = errno; + free(t); + free(*tgt); + *tgt_sz = 0; + errno = error; + return -1; + } + *tgt = u; + *tgt_sz += str_len; + strcat(*tgt, t); + free(t); + return 0; + } +} + +int apol_str_is_only_white_space(const char *str) +{ + size_t len, i; + if (str == NULL) + return 0; + len = strlen(str); + for (i = 0; i < len; i++) { + if (!isspace(str[i])) + return 0; + } + return 1; +} + +int apol_str_strcmp(const void *a, const void *b, void *unused __attribute__ ((unused))) +{ + return strcmp((const char *)a, (const char *)b); +} + +void *apol_str_strdup(const void *elem, void *unused __attribute__ ((unused))) +{ + return strdup((const char *)elem); +} diff --git a/libapol/src/vector-internal.h b/libapol/src/vector-internal.h new file mode 100644 index 0000000..88c4378 --- /dev/null +++ b/libapol/src/vector-internal.h @@ -0,0 +1,36 @@ +/** + * @file + * + * Protected routines for the vector class. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APOL_VECTOR_INTERNAL_H +#define APOL_VECTOR_INTERNAL_H + +/** + * Change the free function of a vector. Currently, this function is + * friends with the BST class; otherwise consider this to be a private + * function. + */ +void vector_set_free_func(apol_vector_t * v, apol_vector_free_func * fr); + +#endif diff --git a/libapol/src/vector.c b/libapol/src/vector.c new file mode 100644 index 0000000..28fc897 --- /dev/null +++ b/libapol/src/vector.c @@ -0,0 +1,457 @@ +/** + * @file + * Contains the implementation of a generic vector. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <apol/vector.h> +#include "vector-internal.h" +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +/** The default initial capacity of a vector; must be a positive integer */ +#define APOL_VECTOR_DFLT_INIT_CAP 10 + +/** + * Generic vector structure. Stores elements as void*. + */ +struct apol_vector +{ + /** The array of element pointers, which will be resized as needed. */ + void **array; + /** The number of elements currently stored in array. */ + size_t size; + /** The actual amount of space in array. This amount will always + * be >= size and will grow exponentially as needed. */ + size_t capacity; + apol_vector_free_func *fr; +}; + +apol_vector_t *apol_vector_create(apol_vector_free_func * fr) +{ + return apol_vector_create_with_capacity(APOL_VECTOR_DFLT_INIT_CAP, fr); +} + +apol_vector_t *apol_vector_create_with_capacity(size_t cap, apol_vector_free_func * fr) +{ + apol_vector_t *v = NULL; + int error; + + if (cap < 1) { + cap = 1; + } + v = calloc(1, sizeof(apol_vector_t)); + if (!v) + return NULL; + v->array = calloc((v->capacity = cap), sizeof(void *)); + if (!(v->array)) { + error = errno; + free(v); + errno = error; + return NULL; + } + v->fr = fr; + return v; +} + +apol_vector_t *apol_vector_create_from_iter(qpol_iterator_t * iter, apol_vector_free_func * fr) +{ + size_t iter_size; + apol_vector_t *v; + void *item; + int error; + if (qpol_iterator_get_size(iter, &iter_size) < 0 || (v = apol_vector_create_with_capacity(iter_size, fr)) == NULL) { + return NULL; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, &item)) { + error = errno; + free(v); + errno = error; + return NULL; + } + apol_vector_append(v, item); + } + return v; +} + +apol_vector_t *apol_vector_create_from_vector(const apol_vector_t * v, apol_vector_dup_func * dup, void *data, + apol_vector_free_func * fr) +{ + apol_vector_t *new_v; + size_t i; + if (v == NULL) { + errno = EINVAL; + return NULL; + } + if ((new_v = apol_vector_create_with_capacity(v->capacity, fr)) == NULL) { + return NULL; + } + if (dup == NULL) { + memcpy(new_v->array, v->array, v->size * sizeof(void *)); + } else { + for (i = 0; i < v->size; i++) { + new_v->array[i] = dup(v->array[i], data); + } + } + new_v->size = v->size; + return new_v; +} + +apol_vector_t *apol_vector_create_from_intersection(const apol_vector_t * v1, + const apol_vector_t * v2, apol_vector_comp_func * cmp, void *data) +{ + apol_vector_t *new_v; + size_t i, j; + if (v1 == NULL || v2 == NULL) { + errno = EINVAL; + return NULL; + } + if ((new_v = apol_vector_create(NULL)) == NULL) { + return NULL; + } + for (i = 0; i < v1->size; i++) { + for (j = 0; j < v2->size; j++) { + if ((cmp != NULL && cmp(v1->array[i], v2->array[j], data) == 0) || + (cmp == NULL && v1->array[i] == v2->array[j])) { + if (apol_vector_append(new_v, v1->array[i]) < 0) { + apol_vector_destroy(&new_v); + return NULL; + } + break; + } + } + } + return new_v; +} + +void apol_vector_destroy(apol_vector_t ** v) +{ + size_t i = 0; + + if (!v || !(*v)) + return; + + if ((*v)->fr) { + for (i = 0; i < (*v)->size; i++) { + (*v)->fr((*v)->array[i]); + } + } + free((*v)->array); + (*v)->array = NULL; + free(*v); + *v = NULL; +} + +size_t apol_vector_get_size(const apol_vector_t * v) +{ + if (!v) { + errno = EINVAL; + return 0; + } else { + return v->size; + } +} + +size_t apol_vector_get_capacity(const apol_vector_t * v) +{ + if (!v) { + errno = EINVAL; + return 0; + } else { + return v->capacity; + } +} + +void *apol_vector_get_element(const apol_vector_t * v, size_t idx) +{ + if (!v || !(v->array)) { + errno = EINVAL; + return NULL; + } + + if (idx >= v->size) { + errno = ERANGE; + return NULL; + } + + return v->array[idx]; +} + +/** + * Grows a vector, by reallocating additional space for it. + * + * @param v Vector to which increase its size. + * + * @return 0 on success, -1 on error. + */ +static int apol_vector_grow(apol_vector_t * v) +{ + void **tmp; + size_t new_capacity = v->capacity; + if (new_capacity >= 128) { + new_capacity += 128; + } else { + new_capacity *= 2; + } + tmp = realloc(v->array, new_capacity * sizeof(void *)); + if (!tmp) { + return -1; + } + v->capacity = new_capacity; + v->array = tmp; + return 0; +} + +int apol_vector_get_index(const apol_vector_t * v, const void *elem, apol_vector_comp_func * cmp, void *data, size_t * i) +{ + if (!v || !i) { + errno = EINVAL; + return -1; + } + + for (*i = 0; *i < v->size; (*i)++) { + if ((cmp != NULL && cmp(v->array[*i], elem, data) == 0) || (cmp == NULL && elem == v->array[*i])) { + return 0; + } + } + return -1; +} + +int apol_vector_append(apol_vector_t * v, void *elem) +{ + if (!v) { + errno = EINVAL; + return -1; + } + + if (v->size >= v->capacity && apol_vector_grow(v)) { + return -1; + } + + v->array[v->size] = elem; + v->size++; + + return 0; +} + +int apol_vector_append_unique(apol_vector_t * v, void *elem, apol_vector_comp_func * cmp, void *data) +{ + size_t i; + if (apol_vector_get_index(v, elem, cmp, data, &i) < 0) { + return apol_vector_append(v, elem); + } + errno = EEXIST; + return 1; +} + +int apol_vector_compare(const apol_vector_t * a, const apol_vector_t * b, apol_vector_comp_func * cmp, void *data, size_t * i) +{ + int compval; + if (a == NULL || b == NULL || i == NULL) { + errno = EINVAL; + return 0; + } + size_t a_len = apol_vector_get_size(a); + size_t b_len = apol_vector_get_size(b); + for (*i = 0; *i < a_len && *i < b_len; (*i)++) { + if (cmp != NULL) { + compval = cmp(a->array[*i], b->array[*i], data); + } else { + compval = (int)((char *)a->array[*i] - (char *)b->array[*i]); + } + if (compval != 0) { + return compval; + } + } + if (a_len == b_len) { + return 0; + } else if (a_len < b_len) { + return -1; + } else { + return 1; + } +} + +static size_t vector_qsort_partition(void **data, size_t first, size_t last, apol_vector_comp_func * cmp, void *arg) +{ + void *pivot = data[last]; + size_t i = first, j = last; + while (i < j) { + if (cmp(data[i], pivot, arg) <= 0) { + i++; + } else { + data[j] = data[i]; + data[i] = data[j - 1]; + j--; + } + } + data[j] = pivot; + return j; +} + +static void vector_qsort(void **data, size_t first, size_t last, apol_vector_comp_func * cmp, void *arg) +{ + if (first < last) { + size_t i = vector_qsort_partition(data, first, last, cmp, arg); + /* need this explicit check here, because i is an + * unsigned integer, and subtracting 1 from 0 is + * bad */ + if (i > 0) { + vector_qsort(data, first, i - 1, cmp, arg); + } + vector_qsort(data, i + 1, last, cmp, arg); + } +} + +/** + * Generic comparison function, which treats elements of the vector as + * unsigned integers. + */ +static int vector_int_comp(const void *a, const void *b, void *data __attribute__ ((unused))) +{ + char *i = (char *)a; + char *j = (char *)b; + if (i < j) { + return -1; + } else if (i > j) { + return 1; + } + return 0; +} + +/* implemented as an in-place quicksort */ +void apol_vector_sort(apol_vector_t * v, apol_vector_comp_func * cmp, void *data) +{ + if (!v) { + errno = EINVAL; + return; + } + if (cmp == NULL) { + cmp = vector_int_comp; + } + if (v->size > 1) { + vector_qsort(v->array, 0, v->size - 1, cmp, data); + } +} + +void apol_vector_sort_uniquify(apol_vector_t * v, apol_vector_comp_func * cmp, void *data) +{ + if (!v) { + errno = EINVAL; + return; + } + if (cmp == NULL) { + cmp = vector_int_comp; + } + if (v->size > 1) { + size_t i, j = 0; + void **new_array; + /* sweep through the array, do a quick compaction, + * then sort */ + for (i = 1; i < v->size; i++) { + if (cmp(v->array[i], v->array[j], data) != 0) { + /* found a unique element */ + j++; + v->array[j] = v->array[i]; + } else { + /* found a non-unique element */ + if (v->fr != NULL) { + v->fr(v->array[i]); + } + } + } + v->size = j + 1; + + apol_vector_sort(v, cmp, data); + j = 0; + for (i = 1; i < v->size; i++) { + if (cmp(v->array[i], v->array[j], data) != 0) { + /* found a unique element */ + j++; + v->array[j] = v->array[i]; + } else { + /* found a non-unique element */ + if (v->fr != NULL) { + v->fr(v->array[i]); + } + } + } + /* try to realloc vector to save space */ + v->size = j + 1; + if ((new_array = realloc(v->array, v->size * sizeof(void *))) != NULL) { + v->array = new_array; + v->capacity = v->size; + } + } +} + +int apol_vector_cat(apol_vector_t * dest, const apol_vector_t * src) +{ + size_t i, orig_size, cap; + void **a; + if (!src || !apol_vector_get_size(src)) { + return 0; /* nothing to append */ + } + + if (!dest) { + errno = EINVAL; + return -1; + } + orig_size = apol_vector_get_size(dest); + for (i = 0; i < apol_vector_get_size(src); i++) + if (apol_vector_append(dest, apol_vector_get_element(src, i))) { + /* revert if possible */ + if (orig_size == 0) { + cap = 1; + } else { + cap = orig_size; + } + a = realloc(dest->array, cap * sizeof(*a)); + if (a != NULL) { + dest->array = a; + } + dest->size = orig_size; + dest->capacity = cap; + return -1; + } + + return 0; +} + +int apol_vector_remove(apol_vector_t * v, const size_t idx) +{ + if (v == NULL || idx >= v->size) { + errno = EINVAL; + return -1; + } + memmove(v->array + idx, v->array + idx + 1, sizeof(v->array[0]) * (v->size - idx - 1)); + v->size--; + return 0; +} + +/******************** friend function below ********************/ + +void vector_set_free_func(apol_vector_t * v, apol_vector_free_func * fr) +{ + v->fr = fr; +} diff --git a/libapol/swig/Makefile.am b/libapol/swig/Makefile.am new file mode 100644 index 0000000..f7c3c06 --- /dev/null +++ b/libapol/swig/Makefile.am @@ -0,0 +1,15 @@ +if DO_SWIGIFY_PYTHON + MAYBE_PYSWIG = python +endif + +if DO_SWIGIFY_JAVA + MAYBE_JSWIG = java +endif + +if DO_SWIGIFY_TCL + MAYBE_TCLSWIG = tcl +endif + +SUBDIRS = $(MAYBE_PYSWIG) $(MAYBE_JSWIG) $(MAYBE_TCLSWIG) + +dist_noinst_DATA = apol.i diff --git a/libapol/swig/apol.i b/libapol/swig/apol.i new file mode 100644 index 0000000..ae1262d --- /dev/null +++ b/libapol/swig/apol.i @@ -0,0 +1,3220 @@ +/** + * @file + * SWIG declarations for libapol. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +%module apol + +%{ +#include <apol/avrule-query.h> +#include <apol/bool-query.h> +#include <apol/bst.h> +#include <apol/class-perm-query.h> +#include <apol/condrule-query.h> +#include <apol/constraint-query.h> +#include <apol/context-query.h> +#include <apol/domain-trans-analysis.h> +#include <apol/fscon-query.h> +#include <apol/infoflow-analysis.h> +#include <apol/isid-query.h> +#include <apol/mls-query.h> +#include <apol/netcon-query.h> +#include <apol/perm-map.h> +#include <apol/policy.h> +#include <apol/policy-path.h> +#include <apol/policy-query.h> +#include <apol/range_trans-query.h> +#include <apol/rbacrule-query.h> +#include <apol/relabel-analysis.h> +#include <apol/render.h> +#include <apol/role-query.h> +#include <apol/terule-query.h> +#include <apol/type-query.h> +#include <apol/types-relation-analysis.h> +#include <apol/user-query.h> +#include <apol/util.h> +#include <apol/vector.h> +#include <errno.h> + +/* Provide hooks so that language-specific modules can define the + * callback function, used by the handler in + * apol_policy_create_from_policy_path(). + */ +SWIGEXPORT apol_callback_fn_t apol_swig_message_callback = NULL; +SWIGEXPORT void * apol_swig_message_callback_arg = NULL; + +%} + +#ifdef SWIGJAVA +%javaconst(1); + +/* get the java environment so we can throw exceptions */ +%{ + static JNIEnv *apol_global_jenv; + jint JNI_OnLoad(JavaVM *vm, void *reserved) { + (*vm)->AttachCurrentThread(vm, (void **)&apol_global_jenv, NULL); + return JNI_VERSION_1_2; + } +%} +#endif + +%include exception.i +%include stdint.i +%import qpol.i + +%{ +#undef BEGIN_EXCEPTION +#undef END_EXCEPTION +%} + +#ifdef SWIGJAVA + +%exception { + apol_global_jenv = jenv; + $action +} + +%{ +#define BEGIN_EXCEPTION JNIEnv *local_jenv = apol_global_jenv; { +#define END_EXCEPTION } +%} +/* handle size_t correctly in java as architecture independent */ +%typemap(jni) size_t "jlong" +%typemap(jtype) size_t "long" +%typemap(jstype) size_t "long" +%typemap("javaimports") SWIGTYPE %{import com.tresys.setools.qpol.*;%} +%typemap(javabody) SWIGTYPE %{ + private long swigCPtr; + protected boolean swigCMemOwn; + + public $javaclassname(long cPtr, boolean cMemoryOwn) { + swigCMemOwn = cMemoryOwn; + swigCPtr = cPtr; + } + + public static long getCPtr($javaclassname obj) { + return (obj == null) ? 0 : obj.swigCPtr; + } +%} +/* the following handles the dependencies on qpol */ +%pragma(java) jniclassimports=%{import com.tresys.setools.qpol.*;%} +%pragma(java) jniclasscode=%{ + static { + try + { + libapol_get_version(); + } + catch (UnsatisfiedLinkError ule) + { + System.loadLibrary("japol"); + } + } +%} +%pragma(java) moduleimports=%{import com.tresys.setools.qpol.*;%} +#else +/* not in java so handle size_t as architecture dependent */ +#ifdef SWIGWORDSIZE64 +typedef uint64_t size_t; +#else +typedef uint32_t size_t; +#endif +%{ +#define BEGIN_EXCEPTION +#define END_EXCEPTION +%} +#endif + +#ifdef SWIGJAVA + +/* if java, pass the new exception macro to C not just SWIG */ +#undef SWIG_exception +#define SWIG_exception(code, msg) {SWIG_JavaException(local_jenv, code, msg); goto fail;} +%inline %{ +#undef SWIG_exception +#define SWIG_exception(code, msg) {SWIG_JavaException(local_jenv, code, msg); goto fail;} +%} +#endif + +#ifdef SWIGTCL +/* implement a custom non thread-safe error handler */ +%{ +static char *message = NULL; +static void tcl_clear_error(void) +{ + free(message); + message = NULL; +} +static void tcl_throw_error(const char *s) +{ + free(message); + message = strdup(s); +} +static char *tcl_get_error(void) +{ + return message; +} +#undef SWIG_exception +#define SWIG_exception(code, msg) {tcl_throw_error(msg); goto fail;} +%} + +%wrapper %{ +/* Tcl module's initialization routine is expected to be named + * Apol_Init(), but the output file will be called libtapol.so instead + * of libapol.so. Therefore add an alias from Tapol_Init() to the + * real Apol_Init(). + */ +SWIGEXPORT int Tapol_Init(Tcl_Interp *interp) { + return SWIG_init(interp); +} +%} + +%exception { + char *err; + tcl_clear_error(); + $action + if ((err = tcl_get_error()) != NULL) { + Tcl_Obj *obj = Tcl_NewStringObj(message, -1); + Tcl_ResetResult(interp); + Tcl_SetObjResult(interp, obj); + goto fail; + } +} +#undef SWIG_exception +#define SWIG_exception(code, msg) {tcl_throw_error(msg); goto fail;} +#endif + + +/* defines from policy-query.h */ +/* Many libapol queries act upon MLS contexts. Use these defines to + * specify set operations upon contexts. + */ +#define APOL_QUERY_SUB 0x02 /* query is subset of rule range */ +#define APOL_QUERY_SUPER 0x04 /* query is superset of rule range */ +#define APOL_QUERY_EXACT (APOL_QUERY_SUB|APOL_QUERY_SUPER) +#define APOL_QUERY_INTERSECT 0x08 /* query overlaps any part of rule range */ +#define APOL_QUERY_FLAGS \ + (APOL_QUERY_SUB | APOL_QUERY_SUPER | APOL_QUERY_EXACT | \ + APOL_QUERY_INTERSECT) +/* The AV rule search and TE rule search use these flags when + * specifying what kind of symbol is being searched. Strings are + * normally interpreted either as a type or as an attribute; the behavior + * can be changed to use only types or only attributes. + */ +#define APOL_QUERY_SYMBOL_IS_TYPE 0x01 +#define APOL_QUERY_SYMBOL_IS_ATTRIBUTE 0x02 + +/* from util.h */ +const char *libapol_get_version(void); +/* defines from netinet/in.h for ip protocols */ +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 +const char *apol_protocol_to_str(uint8_t protocol); +uint8_t apol_str_to_protocol(const char *protocol_str); +%newobject wrap_apol_str_to_internal_ip(char*); +%rename(apol_str_to_internal_ip) wrap_apol_str_to_internal_ip; +%inline %{ + typedef struct apol_ip { + uint32_t ip[4]; + int proto; + } apol_ip_t; + apol_ip_t *wrap_apol_str_to_internal_ip(char *str) { + apol_ip_t *ip = NULL; + BEGIN_EXCEPTION + ip = calloc(1, sizeof(*ip)); + int retv = 0; + if (!ip) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + retv = apol_str_to_internal_ip(str, ip->ip); + if (retv < 0) { + free(ip); + SWIG_exception(SWIG_RuntimeError, "Could not convert string to IP"); + } + ip->proto = retv; + END_EXCEPTION + fail: + return ip; + } +%} +%extend apol_ip_t { + apol_ip_t(const char *str) { + apol_ip_t *ip = NULL; + BEGIN_EXCEPTION + ip = calloc(1, sizeof(*ip)); + int retv = 0; + if (!ip) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + retv = apol_str_to_internal_ip(str, ip->ip); + if (retv < 0) { + free(ip); + SWIG_exception(SWIG_RuntimeError, "Could not convert string to IP"); + } + ip->proto = retv; + END_EXCEPTION + fail: + return ip; + }; + ~apol_ip_t() { + free(self); + }; + int get_protocol() { + return self->proto; + }; +} + +const char *apol_objclass_to_str(uint32_t objclass); +uint32_t apol_str_to_objclass(const char *objclass); +const char *apol_fs_use_behavior_to_str(uint32_t behavior); +int apol_str_to_fs_use_behavior(const char *behavior); +const char *apol_rule_type_to_str(uint32_t rule_type); +const char *apol_cond_expr_type_to_str(uint32_t expr_type); +%newobject apol_file_find_path(const char *); +char *apol_file_find_path(const char *file_name); + +/* directly include and wrap */ +%newobject apol_ipv4_addr_render(const apol_policy_t *p, uint32_t addr[4]); +%newobject apol_ipv6_addr_render(const apol_policy_t *p, uint32_t addr[4]); +%newobject apol_qpol_context_render(const apol_policy_t *p, const qpol_context_t *context); +%include "apol/render.h" + +/* derived vector type here */ +%inline %{ + typedef struct apol_string_vector apol_string_vector_t; +%} +typedef struct apol_vector {} apol_vector_t; +%extend apol_vector_t { + apol_vector_t() { + return apol_vector_create(NULL); + }; + apol_vector_t(qpol_iterator_t *iter) { + return apol_vector_create_from_iter(iter, NULL); + }; + apol_vector_t(apol_vector_t *v) { + return apol_vector_create_from_vector(v, NULL, NULL, NULL); + }; + apol_vector_t(apol_vector_t *a, apol_vector_t *b) { + return apol_vector_create_from_intersection(a, b, NULL, NULL); + }; + size_t get_size() { + return apol_vector_get_size(self); + }; + size_t get_capacity() { + return apol_vector_get_capacity(self); + }; + void *get_element(size_t i) { + return apol_vector_get_element(self, i); + }; + ~apol_vector_t() { + apol_vector_destroy(&self); + }; + void append(void *x) { + BEGIN_EXCEPTION + if (apol_vector_append(self, x)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void append_unique(void *x) { + BEGIN_EXCEPTION + if (apol_vector_append_unique(self, x, NULL, NULL)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void cat(apol_vector_t *src) { + BEGIN_EXCEPTION + if (apol_vector_cat(self, src)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void remove(size_t idx) { + BEGIN_EXCEPTION + if (apol_vector_remove(self, idx)) { + SWIG_exception(SWIG_RuntimeError, "Error removing vector element"); + } + END_EXCEPTION + fail: + return; + }; + void sort() { + apol_vector_sort(self, NULL, NULL); + }; + void sort_uniquify() { + apol_vector_sort_uniquify(self, NULL, NULL); + }; +}; +%rename(apol_vector_compare) wrap_apol_vector_compare; +%inline %{ + int wrap_apol_vector_compare(apol_vector_t *a, apol_vector_t *b) { + size_t idx; /* tracks first difference - currently dropped */ + return apol_vector_compare(a, b, NULL, NULL, &idx); + } +%} +typedef struct apol_string_vector {} apol_string_vector_t; +%extend apol_string_vector_t { + apol_string_vector_t() { + return (apol_string_vector_t*)apol_vector_create(free); + }; + apol_string_vector_t(apol_string_vector_t *v) { + return (apol_string_vector_t*)apol_vector_create_from_vector((apol_vector_t*)v, apol_str_strdup, NULL, free); + }; + apol_string_vector_t(apol_string_vector_t *a, apol_string_vector_t *b) { + return (apol_string_vector_t*)apol_vector_create_from_intersection((apol_vector_t*)a, (apol_vector_t*)b, apol_str_strcmp, NULL); + }; + size_t get_size() { + return apol_vector_get_size((apol_vector_t*)self); + }; + size_t get_capacity() { + return apol_vector_get_capacity((apol_vector_t*)self); + }; + char *get_element(size_t i) { + return (char*)apol_vector_get_element((apol_vector_t*)self, i); + }; + ~apol_string_vector_t() { + apol_vector_destroy((apol_vector_t**)&self); + }; + size_t get_index(char *str) { + size_t idx; + if (apol_vector_get_index((apol_vector_t*)self, str, apol_str_strcmp, NULL, &idx)) + return apol_vector_get_size((apol_vector_t*)self) + 1; + return idx; + }; + void append(char *str) { + BEGIN_EXCEPTION + char *tmp = strdup(str); + if (!tmp || apol_vector_append((apol_vector_t*)self, tmp)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void append_unique(char *str) { + BEGIN_EXCEPTION + char *tmp = strdup(str); + if (!tmp || apol_vector_append_unique((apol_vector_t*)self, tmp, apol_str_strcmp, NULL)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void cat(apol_string_vector_t *src) { + BEGIN_EXCEPTION + if (apol_vector_cat((apol_vector_t*)self, (apol_vector_t*)src)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void remove(size_t idx) { + BEGIN_EXCEPTION + char *x = apol_vector_get_element((apol_vector_t*)self, idx); + if (apol_vector_remove((apol_vector_t*)self, idx)) { + SWIG_exception(SWIG_RuntimeError, "Error removing vector element"); + } + free(x); + END_EXCEPTION + fail: + return; + }; + void sort() { + apol_vector_sort((apol_vector_t*)self, apol_str_strcmp, NULL); + }; + void sort_uniquify() { + apol_vector_sort_uniquify((apol_vector_t*)self, apol_str_strcmp, NULL); + }; +}; + +/* apol policy path */ + typedef enum apol_policy_path_type +{ + APOL_POLICY_PATH_TYPE_MONOLITHIC = 0, + APOL_POLICY_PATH_TYPE_MODULAR +} apol_policy_path_type_e; +typedef struct apol_policy_path {} apol_policy_path_t; +%extend apol_policy_path_t { + apol_policy_path_t(apol_policy_path_type_e type, char * primary, apol_string_vector_t *modules = NULL) { + apol_policy_path_t *p; + BEGIN_EXCEPTION + if ((p = apol_policy_path_create(type, primary, (apol_vector_t*)modules)) == NULL) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return p; + }; + apol_policy_path_t(char *path) { + apol_policy_path_t *p; + BEGIN_EXCEPTION + if ((p = apol_policy_path_create_from_file(path)) == NULL) { + SWIG_exception(SWIG_RuntimeError, "Input/output error"); + } + END_EXCEPTION + fail: + return p; + }; + apol_policy_path_t(char *str, int unused) { + apol_policy_path_t *p; + BEGIN_EXCEPTION + if ((p = apol_policy_path_create_from_string(str)) == NULL) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return p; + }; + apol_policy_path_t(apol_policy_path_t *in) { + apol_policy_path_t *p; + BEGIN_EXCEPTION + if ((p = apol_policy_path_create_from_policy_path(in)) == NULL) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return p; + }; + ~apol_policy_path_t() { + apol_policy_path_destroy(&self); + }; + apol_policy_path_type_e get_type() { + return apol_policy_path_get_type(self); + }; + const char *get_primary() { + return apol_policy_path_get_primary(self); + }; + const apol_string_vector_t *get_modules() { + return (apol_string_vector_t*)apol_policy_path_get_modules(self); + }; + %newobject to_string(); + char *to_string() { + char *str; + BEGIN_EXCEPTION + str = apol_policy_path_to_string(self); + if (!str) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return str; + }; + void to_file(char *path) { + BEGIN_EXCEPTION + if (apol_policy_path_to_file(self, path)) { + SWIG_exception(SWIG_RuntimeError, "Input/outpet error"); + } + END_EXCEPTION + fail: + return; + }; +}; +int apol_policy_path_compare(const apol_policy_path_t * a, const apol_policy_path_t * b); +int apol_file_is_policy_path_list(const char *filename); + +/* apol policy */ +typedef struct apol_policy {} apol_policy_t; +#define APOL_PERMMAP_MAX_WEIGHT 10 +#define APOL_PERMMAP_MIN_WEIGHT 1 +#define APOL_PERMMAP_UNMAPPED 0x00 +#define APOL_PERMMAP_READ 0x01 +#define APOL_PERMMAP_WRITE 0x02 +#define APOL_PERMMAP_BOTH (APOL_PERMMAP_READ | APOL_PERMMAP_WRITE) +#define APOL_PERMMAP_NONE 0x10 +%extend apol_policy_t { + apol_policy_t(apol_policy_path_t *path, int options = 0) { + apol_policy_t *p; + BEGIN_EXCEPTION + p = apol_policy_create_from_policy_path(path, options, apol_swig_message_callback, apol_swig_message_callback_arg); + if (!p) { + if (errno == ENOMEM) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } else { + SWIG_exception(SWIG_IOError, "Failed to create policy"); + } + } + END_EXCEPTION + fail: + return p; + }; + ~apol_policy_t() { + apol_policy_destroy(&self); + }; + int get_policy_type() { + return apol_policy_get_policy_type(self); + }; + qpol_policy_t *get_qpol() { + return apol_policy_get_qpol(self); + }; + int is_mls() { + return apol_policy_is_mls(self); + }; + %newobject get_version_type_mls_str(); + char *get_version_type_mls_str() { + char *str; + BEGIN_EXCEPTION + str = apol_policy_get_version_type_mls_str(self); + if (!str) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return str; + }; + void open_permmap(const char *path) { + BEGIN_EXCEPTION + if (apol_policy_open_permmap(self, path) < 0) { + SWIG_exception(SWIG_RuntimeError, "Error loading permission map"); + } + END_EXCEPTION + fail: + return; + }; + void save_permmap(const char *path) { + BEGIN_EXCEPTION + if (apol_policy_save_permmap(self, path)) { + SWIG_exception(SWIG_RuntimeError, "Could not save permission map"); + } + END_EXCEPTION + fail: + return; + }; + int get_permmap_weight(const char *class_name, const char *perm_name) { + int dir, weight; + BEGIN_EXCEPTION + if (apol_policy_get_permmap(self, class_name, perm_name, &dir, &weight)) { + SWIG_exception(SWIG_RuntimeError, "Could not get permission map weight"); + } + END_EXCEPTION + fail: + return weight; + }; + int get_permmap_direction(char *class_name, char *perm_name) { + int dir, weight; + BEGIN_EXCEPTION + if (apol_policy_get_permmap(self, class_name, perm_name, &dir, &weight)) { + SWIG_exception(SWIG_RuntimeError, "Could not get permission map direction"); + } + END_EXCEPTION + fail: + return dir; + }; + void set_permmap(const char *class_name, const char *perm_name, int direction, int weight) { + BEGIN_EXCEPTION + if (apol_policy_set_permmap(self, class_name, perm_name, direction, weight)) { + SWIG_exception(SWIG_RuntimeError, "Could not set permission mapping"); + } + END_EXCEPTION + fail: + return; + }; + void build_domain_trans_table() { + BEGIN_EXCEPTION + if (apol_policy_build_domain_trans_table(self)) { + SWIG_exception(SWIG_RuntimeError, "Could not build domain transition table"); + } + END_EXCEPTION + fail: + return; + }; + void reset_domain_trans_table() { + apol_policy_reset_domain_trans_table(self); + } +}; + +/* apol type query */ +typedef struct apol_type_query {} apol_type_query_t; +%extend apol_type_query_t { + apol_type_query_t() { + apol_type_query_t *tq; + BEGIN_EXCEPTION + tq = apol_type_query_create(); + if (!tq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return tq; + }; + ~apol_type_query_t() { + apol_type_query_destroy(&self); + }; + %newobject run(apol_policy_t *); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_type_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run type query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_type(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_type_query_set_type(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_regex(apol_policy_t *p, int regex) { + apol_type_query_set_regex(p, self, regex); + }; +}; + +/* apol attribute query */ +typedef struct apol_attr_query {} apol_attr_query_t; +%extend apol_attr_query_t { + apol_attr_query_t() { + apol_attr_query_t *aq; + BEGIN_EXCEPTION + aq = apol_attr_query_create(); + if (!aq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return aq; + }; + ~apol_attr_query_t() { + apol_attr_query_destroy(&self); + }; + %newobject run(apol_policy_t *); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_attr_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run attribute query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_attr(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_attr_query_set_attr(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_regex(apol_policy_t *p, int regex) { + apol_attr_query_set_regex(p, self, regex); + }; +}; + +/* apol role query */ +typedef struct apol_role_query {} apol_role_query_t; +%extend apol_role_query_t { + apol_role_query_t() { + apol_role_query_t *rq; + BEGIN_EXCEPTION + rq = apol_role_query_create(); + if (!rq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return rq; + }; + ~apol_role_query_t() { + apol_role_query_destroy(&self); + }; + %newobject run(apol_policy_t *); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_role_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run role query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_role(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_role_query_set_role(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_type(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_role_query_set_type(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_regex(apol_policy_t *p, int regex) { + apol_role_query_set_regex(p, self, regex); + }; +}; +int apol_role_has_type(apol_policy_t * p, qpol_role_t * r, qpol_type_t * t); + +/* apol class query */ +typedef struct apol_class_query {} apol_class_query_t; +%extend apol_class_query_t { + apol_class_query_t() { + apol_class_query_t *cq; + BEGIN_EXCEPTION + cq = apol_class_query_create(); + if (!cq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return cq; + }; + ~apol_class_query_t() { + apol_class_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_class_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run class query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_class(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_class_query_set_class(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_common(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_class_query_set_common(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_regex(apol_policy_t *p, int regex) { + apol_class_query_set_regex(p, self, regex); + }; +}; + +/* apol common query */ +typedef struct apol_common_query {} apol_common_query_t; +%extend apol_common_query_t { + apol_common_query_t() { + apol_common_query_t *cq; + BEGIN_EXCEPTION + cq = apol_common_query_create(); + if (!cq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return cq; + }; + ~apol_common_query_t() { + apol_common_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_common_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run common query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_common(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_common_query_set_common(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_regex(apol_policy_t *p, int regex) { + apol_common_query_set_regex(p, self, regex); + }; +}; + +/* apol perm query */ +typedef struct apol_perm_query {} apol_perm_query_t; +%extend apol_perm_query_t { + apol_perm_query_t() { + apol_perm_query_t *pq; + BEGIN_EXCEPTION + pq = apol_perm_query_create(); + if (!pq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return pq; + }; + ~apol_perm_query_t() { + apol_perm_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_string_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_perm_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run permission query"); + } + END_EXCEPTION + fail: + return (apol_string_vector_t*)v; + }; + void set_perm(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_perm_query_set_perm(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_regex(apol_policy_t *p, int regex) { + apol_perm_query_set_regex(p, self, regex); + }; +}; + +/* apol bool query */ +typedef struct apol_bool_query {} apol_bool_query_t; +%extend apol_bool_query_t { + apol_bool_query_t() { + apol_bool_query_t *bq; + BEGIN_EXCEPTION + bq = apol_bool_query_create(); + if (!bq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return bq; + }; + ~apol_bool_query_t() { + apol_bool_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_bool_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run boolean query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_bool(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_bool_query_set_bool(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_regex(apol_policy_t *p, int regex) { + apol_bool_query_set_regex(p, self, regex); + }; +}; + +/* apol mls level */ +typedef struct apol_mls_level {} apol_mls_level_t; +%extend apol_mls_level_t { + apol_mls_level_t() { + apol_mls_level_t *aml; + BEGIN_EXCEPTION + aml = apol_mls_level_create(); + if (!aml) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return aml; + }; + apol_mls_level_t(apol_mls_level_t *in) { + apol_mls_level_t *aml; + BEGIN_EXCEPTION + aml = apol_mls_level_create_from_mls_level(in); + if (!aml) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return aml; + }; + apol_mls_level_t(apol_policy_t *p, const char *str) { + apol_mls_level_t *aml; + BEGIN_EXCEPTION + aml = apol_mls_level_create_from_string(p, str); + if (!aml) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return aml; + }; + apol_mls_level_t(const char *str) { + apol_mls_level_t *aml; + BEGIN_EXCEPTION + aml = apol_mls_level_create_from_literal(str); + if (!aml) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return aml; + }; + apol_mls_level_t(apol_policy_t *p, qpol_mls_level_t *qml) { + apol_mls_level_t *aml; + BEGIN_EXCEPTION + aml = apol_mls_level_create_from_qpol_mls_level(p, qml); + if (!aml) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return aml; + }; + apol_mls_level_t(apol_policy_t *p, qpol_level_t *ql) { + apol_mls_level_t *aml; + BEGIN_EXCEPTION + aml = apol_mls_level_create_from_qpol_level_datum(p, ql); + if (!aml) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return aml; + }; + ~apol_mls_level_t() { + apol_mls_level_destroy(&self); + }; + void set_sens(apol_policy_t *p, char *sens) { + BEGIN_EXCEPTION + if (apol_mls_level_set_sens(p, self, sens)) { + SWIG_exception(SWIG_RuntimeError, "Could not set level sensitivity"); + } + END_EXCEPTION + fail: + return; + }; + const char *get_sens() { + return apol_mls_level_get_sens(self); + }; + void append_cats(apol_policy_t *p, char *cats) { + BEGIN_EXCEPTION + if (apol_mls_level_append_cats(p, self, cats)) { + SWIG_exception(SWIG_RuntimeError, "Could not append level category"); + } + END_EXCEPTION + fail: + return; + }; + const apol_string_vector_t *get_cats() { + return (apol_string_vector_t *) apol_mls_level_get_cats(self); + }; + int validate(apol_policy_t *p) { + int ret = -1; + BEGIN_EXCEPTION + ret = apol_mls_level_validate(p, self); + if (ret < 0) { + SWIG_exception(SWIG_ValueError, "Could not validate level"); + } + END_EXCEPTION + fail: + return ret; + } + %newobject render(apol_policy_t*); + char *render(apol_policy_t *p) { + char *str; + BEGIN_EXCEPTION + str = apol_mls_level_render(p, self); + if (!str) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return str; + }; + int convert(apol_policy_t *p) { + int ret = -1; + BEGIN_EXCEPTION + ret = apol_mls_level_convert(p, self); + if (ret < 0) { + SWIG_exception(SWIG_ValueError, "Could not convert level"); + } + END_EXCEPTION + fail: + return ret; + } + int is_literal() { + int ret = -1; + BEGIN_EXCEPTION + ret = apol_mls_level_is_literal(self); + if (ret < 0) { + SWIG_exception(SWIG_ValueError, "Could not determine if level is literal"); + } + END_EXCEPTION + fail: + return ret; + } +}; +#define APOL_MLS_EQ 0 +#define APOL_MLS_DOM 1 +#define APOL_MLS_DOMBY 2 +#define APOL_MLS_INCOMP 3 +int apol_mls_level_compare(apol_policy_t * p, const apol_mls_level_t * level1, const apol_mls_level_t * level2); +int apol_mls_sens_compare(apol_policy_t * p, const char *sens1, const char *sens2); +int apol_mls_cats_compare(apol_policy_t * p, const char *cat1, const char *cat2); +%inline %{ + apol_mls_level_t *apol_mls_level_from_void(void *x) { + return (apol_mls_level_t*)x; + }; +%} + +/* apol mls range */ +#ifdef SWIGPYTHON +%typemap(in) apol_mls_level_t *lvl { + void *x = NULL; + Py_IncRef($input); + SWIG_ConvertPtr($input, &x,SWIGTYPE_p_apol_mls_level, 0 | 0 ); + $1 = (apol_mls_level_t*)x; +} +#endif +typedef struct apol_mls_range {} apol_mls_range_t; +%extend apol_mls_range_t { + apol_mls_range_t() { + apol_mls_range_t *amr; + BEGIN_EXCEPTION + amr = apol_mls_range_create(); + if (!amr) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return amr; + }; + apol_mls_range_t(apol_mls_range_t *in) { + apol_mls_range_t *amr; + BEGIN_EXCEPTION + amr = apol_mls_range_create_from_mls_range(in); + if (!amr) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return amr; + }; + apol_mls_range_t(apol_policy_t *p, const char *s) { + apol_mls_range_t *amr; + BEGIN_EXCEPTION + amr = apol_mls_range_create_from_string(p, s); + if (!amr) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return amr; + }; + apol_mls_range_t(const char *s) { + apol_mls_range_t *amr; + BEGIN_EXCEPTION + amr = apol_mls_range_create_from_literal(s); + if (!amr) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return amr; + }; + apol_mls_range_t(apol_policy_t *p, qpol_mls_range_t *in) { + apol_mls_range_t *amr; + BEGIN_EXCEPTION + amr = apol_mls_range_create_from_qpol_mls_range(p, in); + if (!amr) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return amr; + }; + ~apol_mls_range_t() { + apol_mls_range_destroy(&self); + }; + void set_low(apol_policy_t *p, apol_mls_level_t *lvl) { + BEGIN_EXCEPTION + if (apol_mls_range_set_low(p, self, lvl)) { + SWIG_exception(SWIG_RuntimeError, "Could not set low level"); + } + END_EXCEPTION + fail: + return; + }; + void set_high(apol_policy_t *p, apol_mls_level_t *lvl) { + BEGIN_EXCEPTION + if (apol_mls_range_set_high(p, self, lvl)) { + SWIG_exception(SWIG_RuntimeError, "Could not set high level"); + } + END_EXCEPTION + fail: + return; + }; + const apol_mls_level_t *get_low() { + return apol_mls_range_get_low(self); + } + const apol_mls_level_t *get_high() { + return apol_mls_range_get_high(self); + } + %newobject render(apol_policy_t*); + char *render(apol_policy_t *p) { + char *str; + BEGIN_EXCEPTION + str = apol_mls_range_render(p, self); + if (!str) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return str; + }; + %newobject get_levels(apol_policy_t*); + apol_vector_t *get_levels(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + v = apol_mls_range_get_levels(p, self); + if (!v) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return v; + }; + int validate(apol_policy_t *p) { + int ret = apol_mls_range_validate(p, self); + BEGIN_EXCEPTION + if (ret < 0) { + SWIG_exception(SWIG_ValueError, "Could not validate range"); + } + END_EXCEPTION + fail: + return ret; + } + int is_literal() { + int ret = -1; + BEGIN_EXCEPTION + ret = apol_mls_range_is_literal(self); + if (ret < 0) { + SWIG_exception(SWIG_ValueError, "Could not determine if range is literal"); + } + END_EXCEPTION + fail: + return ret; + } + int convert(apol_policy_t *p) { + int ret = -1; + BEGIN_EXCEPTION + ret = apol_mls_range_convert(p, self); + if (ret < 0) { + SWIG_exception(SWIG_ValueError, "Could not convert range"); + } + END_EXCEPTION + fail: + return ret; + } +}; +int apol_mls_range_compare(apol_policy_t * p, const apol_mls_range_t * target, const apol_mls_range_t * search, unsigned int range_compare_type); +int apol_mls_range_contain_subrange(apol_policy_t * p, const apol_mls_range_t * range, const apol_mls_range_t * subrange); +%inline %{ + apol_mls_range_t *apol_mls_range_from_void(void *x) { + return (apol_mls_range_t*)x; + }; +%} + +/* apol level query */ +typedef struct apol_level_query {} apol_level_query_t; +%extend apol_level_query_t { + apol_level_query_t() { + apol_level_query_t * alq; + BEGIN_EXCEPTION + alq = apol_level_query_create(); + if (!alq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return alq; + }; + ~apol_level_query_t() { + apol_level_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_level_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run level query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_sens(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_level_query_set_sens(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_cat(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_level_query_set_cat(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_regex(apol_policy_t *p, int regex) { + apol_level_query_set_regex(p, self, regex); + }; +}; + +/* apol cat query */ +typedef struct apol_cat_query {} apol_cat_query_t; +%extend apol_cat_query_t { + apol_cat_query_t() { + apol_cat_query_t * acq; + BEGIN_EXCEPTION + acq = apol_cat_query_create(); + if (!acq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return acq; + }; + ~apol_cat_query_t() { + apol_cat_query_destroy(&self); + }; + %newobject run(apol_policy_t *); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_cat_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run category query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_cat(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_cat_query_set_cat(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_regex(apol_policy_t *p, int regex) { + apol_cat_query_set_regex(p, self, regex); + }; +}; + +/* apol user query */ +#ifdef SWIGPYTHON +%typemap(in) apol_mls_range_t *rng { + void *x = NULL; + Py_IncRef($input); + SWIG_ConvertPtr($input, &x,SWIGTYPE_p_apol_mls_range, 0 | 0 ); + $1 = (apol_mls_range_t*)x; +} +#endif +typedef struct apol_user_query {} apol_user_query_t; +%extend apol_user_query_t { + apol_user_query_t() { + apol_user_query_t *auq; + BEGIN_EXCEPTION + auq = apol_user_query_create(); + if (!auq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return auq; + }; + ~apol_user_query_t() { + apol_user_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_user_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run user query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_user(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_user_query_set_user(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_role(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_user_query_set_role(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_default_level(apol_policy_t *p, apol_mls_level_t *lvl) { + BEGIN_EXCEPTION + if (apol_user_query_set_default_level(p, self, lvl)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_range(apol_policy_t *p, apol_mls_range_t *rng, int range_match) { + BEGIN_EXCEPTION + if (apol_user_query_set_range(p, self, rng, range_match)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_regex(apol_policy_t *p, int regex) { + apol_user_query_set_regex(p, self, regex); + }; +}; + +/* apol context */ +typedef struct apol_context {} apol_context_t; +%extend apol_context_t { + apol_context_t() { + apol_context_t *ctx; + BEGIN_EXCEPTION + ctx = apol_context_create(); + if (!ctx) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return ctx; + }; + apol_context_t(apol_policy_t *p, qpol_context_t *in) { + apol_context_t *ctx; + BEGIN_EXCEPTION + ctx = apol_context_create_from_qpol_context(p, in); + if (!ctx) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return ctx; + }; + apol_context_t(const char *str) { + apol_context_t *ctx; + BEGIN_EXCEPTION + ctx = apol_context_create_from_literal(str); + if (!ctx) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return ctx; + }; + ~apol_context_t() { + apol_context_destroy(&self); + }; + void set_user(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_context_set_user(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + const char *get_user() { + return apol_context_get_user(self); + }; + void set_role(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_context_set_role(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + const char *get_role() { + return apol_context_get_role(self); + }; + void set_type(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_context_set_type(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + const char *get_type() { + return apol_context_get_type(self); + }; + void set_range(apol_policy_t *p, apol_mls_range_t *rng) { + BEGIN_EXCEPTION + if (apol_context_set_range(p, self, rng)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + const apol_mls_range_t *get_range() { + return apol_context_get_range(self); + }; + int validate(apol_policy_t *p) { + int ret = -1; + BEGIN_EXCEPTION + ret = apol_context_validate(p, self); + if (ret < 0) { + SWIG_exception(SWIG_ValueError, "Could not validate context"); + } + END_EXCEPTION + fail: + return ret; + } + int validate_partial(apol_policy_t *p) { + int ret = -1; + BEGIN_EXCEPTION + ret = apol_context_validate_partial(p, self); + if (ret < 0) { + SWIG_exception(SWIG_ValueError, "Could not validate context"); + } + END_EXCEPTION + fail: + return ret; + } + %newobject render(apol_policy_t*); + char *render(apol_policy_t *p) { + char *str; + BEGIN_EXCEPTION + str = apol_context_render(p, self); + if (!str) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return str; + }; + int convert(apol_policy_t *p) { + int ret = -1; + BEGIN_EXCEPTION + ret = apol_context_convert(p, self); + if (ret < 0) { + SWIG_exception(SWIG_ValueError, "Could not convert context"); + } + END_EXCEPTION + fail: + return ret; + } +}; +int apol_context_compare(apol_policy_t * p, apol_context_t * target, apol_context_t * search, unsigned int range_compare_type); + +/* apol constraint query */ +typedef struct apol_constraint_query {} apol_constraint_query_t; +%extend apol_constraint_query_t { + apol_constraint_query_t() { + apol_constraint_query_t *acq; + BEGIN_EXCEPTION + acq = apol_constraint_query_create(); + if (!acq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return acq; + }; + ~apol_constraint_query_t() { + apol_constraint_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_constraint_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run constraint query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_class(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_constraint_query_set_class(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + } + void set_perm(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_constraint_query_set_perm(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + } + void set_regex(apol_policy_t *p, int regex) { + apol_constraint_query_set_regex(p, self, regex); + }; +}; + +/* apol validatetrans query */ +typedef struct apol_validatetrans_query {} apol_validatetrans_query_t; +%extend apol_validatetrans_query_t { + apol_validatetrans_query_t() { + apol_validatetrans_query_t *avq; + BEGIN_EXCEPTION + avq = apol_validatetrans_query_create(); + if (!avq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return avq; + }; + ~apol_validatetrans_query_t() { + apol_validatetrans_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_validatetrans_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run validatetrans query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_class(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_validatetrans_query_set_class(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + } + void set_regex(apol_policy_t *p, int regex) { + apol_validatetrans_query_set_regex(p, self, regex); + }; +}; + +/* apol genfscon query */ +#ifdef SWIGPYTHON +%typemap(in) apol_context_t *ctx { + void *x = NULL; + Py_IncRef($input); + SWIG_ConvertPtr($input, &x,SWIGTYPE_p_apol_context, 0 | 0 ); + $1 = (apol_context_t*)x; +} +#endif +typedef struct apol_genfscon_query {} apol_genfscon_query_t; +%extend apol_genfscon_query_t { + apol_genfscon_query_t() { + apol_genfscon_query_t *agq; + BEGIN_EXCEPTION + agq = apol_genfscon_query_create(); + if (!agq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return agq; + }; + ~apol_genfscon_query_t() { + apol_genfscon_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_genfscon_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run validatetrans query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_filesystem(apol_policy_t *p, char *fs) { + BEGIN_EXCEPTION + if (apol_genfscon_query_set_filesystem(p, self, fs)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_path(apol_policy_t *p, char *path) { + BEGIN_EXCEPTION + if (apol_genfscon_query_set_path(p, self, path)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_objclass(apol_policy_t *p, int objclass) { + BEGIN_EXCEPTION + if (apol_genfscon_query_set_objclass(p, self, objclass)) { + SWIG_exception(SWIG_RuntimeError, "Could not set object class for genfscon query"); + } + END_EXCEPTION + fail: + return; + }; + void set_context(apol_policy_t *p, apol_context_t *ctx, int range_match) { + apol_genfscon_query_set_context(p, self, ctx, range_match); + }; +}; +%newobject apol_genfscon_render(apol_policy_t *, qpol_genfscon_t *); +char *apol_genfscon_render(apol_policy_t * p, qpol_genfscon_t * genfscon); + +/* apol fs_use query */ +typedef struct apol_fs_use_query {} apol_fs_use_query_t; +%extend apol_fs_use_query_t { + apol_fs_use_query_t() { + apol_fs_use_query_t *afq; + BEGIN_EXCEPTION + afq = apol_fs_use_query_create(); + if (!afq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return afq; + }; + ~apol_fs_use_query_t() { + apol_fs_use_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_fs_use_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run fs_use query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_filesystem(apol_policy_t *p, char *fs) { + BEGIN_EXCEPTION + if (apol_fs_use_query_set_filesystem(p, self, fs)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_behavior(apol_policy_t *p, int behavior) { + BEGIN_EXCEPTION + if (apol_fs_use_query_set_behavior(p, self, behavior)) { + SWIG_exception(SWIG_RuntimeError, "Could not set behavior for fs_use query"); + } + END_EXCEPTION + fail: + return; + }; + void set_context(apol_policy_t *p, apol_context_t *ctx, int range_match) { + apol_fs_use_query_set_context(p, self, ctx, range_match); + }; +}; +%newobject apol_fs_use_render(apol_policy_t*, qpol_fs_use_t*); +char *apol_fs_use_render(apol_policy_t * p, qpol_fs_use_t * fsuse); + +/* apol initial sid query */ +typedef struct apol_isid_query {} apol_isid_query_t; +%extend apol_isid_query_t { + apol_isid_query_t() { + apol_isid_query_t *aiq; + BEGIN_EXCEPTION + aiq = apol_isid_query_create(); + if (!aiq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return aiq; + }; + ~apol_isid_query_t() { + apol_isid_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_isid_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run initial sid query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_name(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_isid_query_set_name(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_context(apol_policy_t *p, apol_context_t *ctx, int range_match) { + apol_isid_query_set_context(p, self, ctx, range_match); + }; +}; + +/* apol portcon query */ +typedef struct apol_portcon_query {} apol_portcon_query_t; +%extend apol_portcon_query_t { + apol_portcon_query_t() { + apol_portcon_query_t *apq; + BEGIN_EXCEPTION + apq = apol_portcon_query_create(); + if (!apq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return apq; + }; + ~apol_portcon_query_t() { + apol_portcon_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_portcon_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run portcon query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_protocol(apol_policy_t *p, int protocol) { + apol_portcon_query_set_protocol(p, self, protocol); + }; + void set_low(apol_policy_t *p, int port) { + apol_portcon_query_set_low(p, self, port); + }; + void set_high(apol_policy_t *p, int port) { + apol_portcon_query_set_high(p, self, port); + }; + void set_context(apol_policy_t *p, apol_context_t *ctx, int range_match) { + apol_portcon_query_set_context(p, self, ctx, range_match); + }; +}; +%newobject apol_portcon_render(apol_policy_t*, qpol_portcon_t*); +char *apol_portcon_render(apol_policy_t * p, qpol_portcon_t * portcon); + +/* apol netifcon query */ +typedef struct apol_netifcon_query {} apol_netifcon_query_t; +%extend apol_netifcon_query_t { + apol_netifcon_query_t() { + apol_netifcon_query_t *anq; + BEGIN_EXCEPTION + anq = apol_netifcon_query_create(); + if (!anq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return anq; + }; + ~apol_netifcon_query_t() { + apol_netifcon_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_netifcon_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run netifcon query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_device(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_netifcon_query_set_device(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_if_context(apol_policy_t *p, apol_context_t *ctx, int range_match) { + apol_netifcon_query_set_if_context(p, self, ctx, range_match); + }; + void set_msg_context(apol_policy_t *p, apol_context_t *ctx, int range_match) { + apol_netifcon_query_set_msg_context(p, self, ctx, range_match); + }; +}; +%newobject apol_netifcon_render(apol_policy_t*, qpol_netifcon_t*); +char *apol_netifcon_render(apol_policy_t * p, qpol_netifcon_t * netifcon); + +/* apol nodecon query */ +typedef struct apol_nodecon_query {} apol_nodecon_query_t; +%extend apol_nodecon_query_t { + apol_nodecon_query_t() { + apol_nodecon_query_t *anq; + BEGIN_EXCEPTION + anq = apol_nodecon_query_create(); + if (!anq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return anq; + }; + ~apol_nodecon_query_t() { + apol_nodecon_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_nodecon_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run nodecon query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_protocol(apol_policy_t *p, int protocol) { + BEGIN_EXCEPTION + if (apol_nodecon_query_set_protocol(p, self, protocol)) { + SWIG_exception(SWIG_RuntimeError, "Could not set protocol for nodecon query"); + } + END_EXCEPTION + fail: + return; + }; + void set_addr(apol_policy_t *p, uint32_t *addr, int protocol) { + BEGIN_EXCEPTION + if (apol_nodecon_query_set_addr(p, self, addr, protocol)) { + SWIG_exception(SWIG_RuntimeError, "Could not set address for nodecon query"); + } + END_EXCEPTION + fail: + return; + }; + void set_addr(apol_policy_t *p, apol_ip_t *addr) { + BEGIN_EXCEPTION + if (apol_nodecon_query_set_addr(p, self, addr->ip, addr->proto)) { + SWIG_exception(SWIG_RuntimeError, "Could not set address for nodecon query"); + } + END_EXCEPTION + fail: + return; + }; + void set_mask(apol_policy_t *p, uint32_t *mask, int protocol) { + BEGIN_EXCEPTION + if (apol_nodecon_query_set_mask(p, self, mask, protocol)) { + SWIG_exception(SWIG_RuntimeError, "Could not set mask for nodecon query"); + } + END_EXCEPTION + fail: + return; + }; + void set_mask(apol_policy_t *p, apol_ip_t *mask) { + BEGIN_EXCEPTION + if (apol_nodecon_query_set_mask(p, self, mask->ip, mask->proto)) { + SWIG_exception(SWIG_RuntimeError, "Could not set mask for nodecon query"); + } + END_EXCEPTION + fail: + return; + }; + void set_context(apol_policy_t *p, apol_context_t *ctx, int range_match) { + apol_nodecon_query_set_context(p, self, ctx, range_match); + }; +}; +%newobject apol_nodecon_render(apol_policy_t*, qpol_nodecon_t*); +char *apol_nodecon_render(apol_policy_t * p, qpol_nodecon_t * nodecon); + +/* apol avrule query */ +typedef struct apol_avrule_query {} apol_avrule_query_t; +%extend apol_avrule_query_t { + apol_avrule_query_t() { + apol_avrule_query_t *avq; + BEGIN_EXCEPTION + avq = apol_avrule_query_create(); + if (!avq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return avq; + }; + ~apol_avrule_query_t() { + apol_avrule_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_avrule_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run avrule query"); + } + END_EXCEPTION + fail: + return v; + }; + %newobject run_syn(apol_policy_t*); + apol_vector_t *run_syn(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_syn_avrule_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run syn avrule query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_rules(apol_policy_t *p, int rules) { + apol_avrule_query_set_rules(p, self, rules); + }; + void set_source(apol_policy_t *p, char *name, int indirect) { + BEGIN_EXCEPTION + if (apol_avrule_query_set_source(p, self, name, indirect)) { + SWIG_exception(SWIG_RuntimeError, "Could not set source for avrule query"); + } + END_EXCEPTION + fail: + return; + }; + void set_source_component(apol_policy_t *p, int component) { + BEGIN_EXCEPTION + if (apol_avrule_query_set_source_component(p, self, component)) { + SWIG_exception(SWIG_RuntimeError, "Could not set source component for avrule query"); + } + END_EXCEPTION + fail: + return; + }; + void set_target(apol_policy_t *p, char *name, int indirect) { + BEGIN_EXCEPTION + if (apol_avrule_query_set_target(p, self, name, indirect)) { + SWIG_exception(SWIG_RuntimeError, "Could not set target for avrule query"); + } + END_EXCEPTION + fail: + return; + }; + void set_target_component(apol_policy_t *p, int component) { + BEGIN_EXCEPTION + if (apol_avrule_query_set_target_component(p, self, component)) { + SWIG_exception(SWIG_RuntimeError, "Could not set target component for avrule query"); + } + END_EXCEPTION + fail: + return; + }; + void append_class(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_avrule_query_append_class(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not append class to avrule query"); + } + END_EXCEPTION + fail: + return; + }; + void append_perm(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_avrule_query_append_perm(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not append permission to avrule query"); + } + END_EXCEPTION + fail: + return; + }; + void set_bool(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_avrule_query_set_bool(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not set boolean for avrule query"); + } + END_EXCEPTION + fail: + return; + }; + void set_enabled(apol_policy_t *p, int enabled) { + apol_avrule_query_set_enabled(p, self, enabled); + }; + void set_all_perms(apol_policy_t *p, int all_perms) { + apol_avrule_query_set_all_perms(p, self, all_perms); + }; + void set_source_any(apol_policy_t *p, int is_any) { + apol_avrule_query_set_source_any(p, self, is_any); + }; + void set_regex(apol_policy_t *p, int regex) { + apol_avrule_query_set_regex(p, self, regex); + }; +}; +%newobject apol_avrule_render(apol_policy_t*, qpol_avrule_t*); +char *apol_avrule_render(apol_policy_t * policy, qpol_avrule_t * rule); +%newobject apol_syn_avrule_render(apol_policy_t*, qpol_syn_avrule_t*); +char *apol_syn_avrule_render(apol_policy_t * policy, qpol_syn_avrule_t * rule); +%newobject wrap_apol_avrule_to_syn_avrules(apol_policy_t*, qpol_avrule_t*, apol_string_vector_t*); +%rename(apol_avrule_to_syn_avrules) wrap_apol_avrule_to_syn_avrules; +%newobject wrap_apol_avrule_list_to_syn_avrules(apol_policy_t*, apol_vector_t*, apol_string_vector_t*); +%rename(apol_avrule_list_to_syn_avrules) wrap_apol_avrule_list_to_syn_avrules; +%inline %{ + apol_vector_t *wrap_apol_avrule_to_syn_avrules(apol_policy_t *p, qpol_avrule_t *rule, apol_string_vector_t *perms) { + apol_vector_t *v; + BEGIN_EXCEPTION + v = apol_avrule_to_syn_avrules(p, rule, (apol_vector_t*)perms); + if (!v) { + SWIG_exception(SWIG_RuntimeError, "Could not convert avrule to syntactic avrules"); + } + END_EXCEPTION + fail: + return v; + } + apol_vector_t *wrap_apol_avrule_list_to_syn_avrules(apol_policy_t *p, apol_vector_t *rules, apol_string_vector_t *perms) { + apol_vector_t *v; + BEGIN_EXCEPTION + v = apol_avrule_list_to_syn_avrules(p, rules, (apol_vector_t*)perms); + if (!v) { + SWIG_exception(SWIG_RuntimeError, "Could not convert avrules to syntactic avrules"); + } + END_EXCEPTION + fail: + return v; + } +%} + +/* apol terule query */ +typedef struct apol_terule_query {} apol_terule_query_t; +%extend apol_terule_query_t { + apol_terule_query_t() { + apol_terule_query_t *atq; + BEGIN_EXCEPTION + atq = apol_terule_query_create(); + if (!atq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return atq; + }; + ~apol_terule_query_t() { + apol_terule_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_terule_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run terule query"); + } + END_EXCEPTION + fail: + return v; + }; + %newobject run_syn(apol_policy_t*); + apol_vector_t *run_syn(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_syn_terule_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run terule query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_rules(apol_policy_t *p, int rules) { + apol_terule_query_set_rules(p, self, rules); + }; + void set_source(apol_policy_t *p, char *name, int indirect) { + BEGIN_EXCEPTION + if (apol_terule_query_set_source(p, self, name, indirect)) { + SWIG_exception(SWIG_RuntimeError, "Could not set source for terule query"); + } + END_EXCEPTION + fail: + return; + }; + void set_source_component(apol_policy_t *p, int component) { + BEGIN_EXCEPTION + if (apol_terule_query_set_source_component(p, self, component)) { + SWIG_exception(SWIG_RuntimeError, "Could not set source component for terule query"); + } + END_EXCEPTION + fail: + return; + }; + void set_target(apol_policy_t *p, char *name, int indirect) { + BEGIN_EXCEPTION + if (apol_terule_query_set_target(p, self, name, indirect)) { + SWIG_exception(SWIG_RuntimeError, "Could not set target for terule query"); + } + END_EXCEPTION + fail: + return; + }; + void set_target_component(apol_policy_t *p, int component) { + BEGIN_EXCEPTION + if (apol_terule_query_set_target_component(p, self, component)) { + SWIG_exception(SWIG_RuntimeError, "Could not set target component for terule query"); + } + END_EXCEPTION + fail: + return; + }; + void append_class(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_terule_query_append_class(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not append class to terule query"); + } + END_EXCEPTION + fail: + return; + }; + void set_default(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_terule_query_set_default(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not set default for terule query"); + } + END_EXCEPTION + fail: + return; + }; + void set_bool(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_terule_query_set_bool(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not set boolean for terule query"); + } + END_EXCEPTION + fail: + return; + }; + void set_enabled(apol_policy_t *p, int enabled) { + apol_terule_query_set_enabled(p, self, enabled); + }; + void set_source_any(apol_policy_t *p, int is_any) { + apol_terule_query_set_source_any(p, self, is_any); + }; + void set_regex(apol_policy_t *p, int regex) { + apol_terule_query_set_regex(p, self, regex); + }; +}; +%newobject apol_terule_render(apol_policy_t*, qpol_terule_t*); +char *apol_terule_render(apol_policy_t * policy, qpol_terule_t * rule); +%newobject apol_syn_terule_render(apol_policy_t*, qpol_syn_terule_t*); +char *apol_syn_terule_render(apol_policy_t * policy, qpol_syn_terule_t * rule); +%newobject apol_terule_to_syn_terules(apol_policy_t*, qpol_terule_t*); +apol_vector_t *apol_terule_to_syn_terules(apol_policy_t * p, qpol_terule_t * rule); +%newobject apol_terule_list_to_syn_terules(apol_policy_t*, apol_vector_t*); +apol_vector_t *apol_terule_list_to_syn_terules(apol_policy_t * p, apol_vector_t * rules); + +/* apol cond rule query */ +typedef struct apol_cond_query {} apol_cond_query_t; +%extend apol_cond_query_t { + apol_cond_query_t() { + apol_cond_query_t *acq; + BEGIN_EXCEPTION + acq = apol_cond_query_create(); + if (!acq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return acq; + }; + ~apol_cond_query_t() { + apol_cond_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_cond_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run condiional query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_bool(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_cond_query_set_bool(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not set boolean for condiional query"); + } + END_EXCEPTION + fail: + return; + }; + void set_regex(apol_policy_t *p, int regex) { + apol_cond_query_set_regex(p, self, regex); + }; +}; +%newobject apol_cond_expr_render(apol_policy_t*, qpol_cond_t*); +char *apol_cond_expr_render(apol_policy_t * p, qpol_cond_t * cond); + +/* apol role allow query */ +typedef struct apol_role_allow_query {} apol_role_allow_query_t; +%extend apol_role_allow_query_t { + apol_role_allow_query_t() { + apol_role_allow_query_t *arq; + BEGIN_EXCEPTION + arq = apol_role_allow_query_create(); + if (!arq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return arq; + }; + ~apol_role_allow_query_t() { + apol_role_allow_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_role_allow_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run role allow query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_source(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_role_allow_query_set_source(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_target(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_role_allow_query_set_target(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_source_any(apol_policy_t *p, int is_any) { + apol_role_allow_query_set_source_any(p, self, is_any); + }; + void set_regex(apol_policy_t *p, int regex) { + apol_role_allow_query_set_regex(p, self, regex); + }; +}; +%newobject apol_role_allow_render(apol_policy_t*, qpol_role_allow_t*); +char *apol_role_allow_render(apol_policy_t * policy, qpol_role_allow_t * rule); + +/* apol role transition rule query */ +typedef struct apol_role_trans_query {} apol_role_trans_query_t; +%extend apol_role_trans_query_t { + apol_role_trans_query_t() { + apol_role_trans_query_t *arq; + BEGIN_EXCEPTION + arq = apol_role_trans_query_create(); + if (!arq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return arq; + }; + ~apol_role_trans_query_t() { + apol_role_trans_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_role_trans_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run role transition query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_source(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_role_trans_query_set_source(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_target(apol_policy_t *p, char *name, int indirect) { + BEGIN_EXCEPTION + if (apol_role_trans_query_set_target(p, self, name, indirect)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_default(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_role_trans_query_set_default(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_source_any(apol_policy_t *p, int is_any) { + apol_role_trans_query_set_source_any(p, self, is_any); + }; + void set_regex(apol_policy_t *p, int regex) { + apol_role_trans_query_set_regex(p, self, regex); + }; +}; +%newobject apol_role_trans_render(apol_policy_t*, qpol_role_trans_t*); +char *apol_role_trans_render(apol_policy_t * policy, qpol_role_trans_t * rule); + +/* apol range transition rule query */ +typedef struct apol_range_trans_query {} apol_range_trans_query_t; +%extend apol_range_trans_query_t { + apol_range_trans_query_t() { + apol_range_trans_query_t *arq; + BEGIN_EXCEPTION + arq = apol_range_trans_query_create(); + if (!arq) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return arq; + }; + ~apol_range_trans_query_t() { + apol_range_trans_query_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_range_trans_get_by_query(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run range transition query"); + } + END_EXCEPTION + fail: + return v; + }; + void set_source(apol_policy_t *p, char *name, int indirect) { + BEGIN_EXCEPTION + if (apol_range_trans_query_set_source(p, self, name, indirect)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_target(apol_policy_t *p, char *name, int indirect) { + BEGIN_EXCEPTION + if (apol_range_trans_query_set_target(p, self, name, indirect)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void append_class(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_range_trans_query_append_class(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not append class to range transition query"); + } + END_EXCEPTION + fail: + return; + }; + void set_range(apol_policy_t *p, apol_mls_range_t *rng, int range_match) { + BEGIN_EXCEPTION + if (apol_range_trans_query_set_range(p, self, rng, range_match)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_source_any(apol_policy_t *p, int is_any) { + apol_range_trans_query_set_source_any(p, self, is_any); + }; + void set_regex(apol_policy_t *p, int regex) { + apol_range_trans_query_set_regex(p, self, regex); + }; +}; +%newobject apol_range_trans_render(apol_policy_t*, qpol_range_trans_t*); +char *apol_range_trans_render(apol_policy_t * policy, qpol_range_trans_t * rule); + +/* domain transition analysis */ +#define APOL_DOMAIN_TRANS_DIRECTION_FORWARD 0x01 +#define APOL_DOMAIN_TRANS_DIRECTION_REVERSE 0x02 +#define APOL_DOMAIN_TRANS_SEARCH_VALID 0x01 +#define APOL_DOMAIN_TRANS_SEARCH_INVALID 0x02 +#define APOL_DOMAIN_TRANS_SEARCH_BOTH (APOL_DOMAIN_TRANS_SEARCH_VALID|APOL_DOMAIN_TRANS_SEARCH_INVALID) +typedef struct apol_domain_trans_analysis {} apol_domain_trans_analysis_t; +%extend apol_domain_trans_analysis_t { + apol_domain_trans_analysis_t() { + apol_domain_trans_analysis_t *dta; + BEGIN_EXCEPTION + dta = apol_domain_trans_analysis_create(); + if (!dta) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return dta; + }; + ~apol_domain_trans_analysis_t() { + apol_domain_trans_analysis_destroy(&self); + }; + void set_direction(apol_policy_t *p, int direction) { + BEGIN_EXCEPTION + if (apol_domain_trans_analysis_set_direction(p, self, (unsigned char)direction)) { + SWIG_exception(SWIG_RuntimeError, "Could not set direction for domain transition analysis"); + } + END_EXCEPTION + fail: + return; + }; + void set_valid(apol_policy_t *p, int valid) { + BEGIN_EXCEPTION + if (apol_domain_trans_analysis_set_valid(p, self, (unsigned char)valid)) { + SWIG_exception(SWIG_RuntimeError, "Could not set valid flag for domain transition analysis"); + } + END_EXCEPTION + fail: + return; + }; + void set_start_type(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_domain_trans_analysis_set_start_type(p, self, name)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void set_result_regex(apol_policy_t *p, char *regex) { + BEGIN_EXCEPTION + if (apol_domain_trans_analysis_set_result_regex(p, self, regex)) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return; + }; + void append_access_type(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_domain_trans_analysis_append_access_type(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not append access type for domain transition analysis"); + } + END_EXCEPTION + fail: + return; + }; + void append_class(apol_policy_t *p, char *class_name) { + BEGIN_EXCEPTION + if (apol_domain_trans_analysis_append_class(p, self, class_name)) { + SWIG_exception(SWIG_RuntimeError, "Could not append access class for domain transition analysis"); + } + END_EXCEPTION + fail: + return; + }; + void append_perm(apol_policy_t *p, char *perm_name) { + BEGIN_EXCEPTION + if (apol_domain_trans_analysis_append_perm(p, self, perm_name)) { + SWIG_exception(SWIG_RuntimeError, "Could not append access permission for domain transition analysis"); + } + END_EXCEPTION + fail: + return; + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v = NULL; + BEGIN_EXCEPTION + if (apol_domain_trans_analysis_do(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run domain transition analysis"); + } + END_EXCEPTION + fail: + return v; + }; +}; +typedef struct apol_domain_trans_result {} apol_domain_trans_result_t; +%extend apol_domain_trans_result_t { + apol_domain_trans_result_t(apol_domain_trans_result_t *in) { + apol_domain_trans_result_t *dtr; + BEGIN_EXCEPTION + dtr = apol_domain_trans_result_create_from_domain_trans_result(in); + if (!dtr) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return dtr; + }; + ~apol_domain_trans_result_t() { + apol_domain_trans_result_destroy(&self); + }; + const qpol_type_t *get_start_type() { + return apol_domain_trans_result_get_start_type(self); + }; + const qpol_type_t *get_entrypoint_type() { + return apol_domain_trans_result_get_entrypoint_type(self); + }; + const qpol_type_t *get_end_type() { + return apol_domain_trans_result_get_end_type(self); + }; + int get_is_valid() { + return apol_domain_trans_result_is_trans_valid(self); + }; + const apol_vector_t *get_proc_trans_rules() { + return apol_domain_trans_result_get_proc_trans_rules(self); + }; + const apol_vector_t *get_entrypoint_rules() { + return apol_domain_trans_result_get_entrypoint_rules(self); + }; + const apol_vector_t *get_exec_rules() { + return apol_domain_trans_result_get_exec_rules(self); + }; + const apol_vector_t *get_setexec_rules() { + return apol_domain_trans_result_get_setexec_rules(self); + }; + const apol_vector_t *get_type_trans_rules() { + return apol_domain_trans_result_get_type_trans_rules(self); + }; + const apol_vector_t *get_access_rules() { + return apol_domain_trans_result_get_access_rules(self); + }; +}; +#define APOL_DOMAIN_TRANS_RULE_PROC_TRANS 0x01 +#define APOL_DOMAIN_TRANS_RULE_EXEC 0x02 +#define APOL_DOMAIN_TRANS_RULE_EXEC_NO_TRANS 0x04 +#define APOL_DOMAIN_TRANS_RULE_ENTRYPOINT 0x08 +#define APOL_DOMAIN_TRANS_RULE_TYPE_TRANS 0x10 +#define APOL_DOMAIN_TRANS_RULE_SETEXEC 0x20 +int apol_domain_trans_table_verify_trans(apol_policy_t * policy, qpol_type_t * start_dom, qpol_type_t * ep_type, qpol_type_t * end_dom); +%inline %{ + apol_domain_trans_result_t *apol_domain_trans_result_from_void(void *x) { + return (apol_domain_trans_result_t*)x; + }; +%} + +/* apol infoflow analysis */ +#define APOL_INFOFLOW_MODE_DIRECT 0x01 +#define APOL_INFOFLOW_MODE_TRANS 0x02 +#define APOL_INFOFLOW_IN 0x01 +#define APOL_INFOFLOW_OUT 0x02 +#define APOL_INFOFLOW_BOTH (APOL_INFOFLOW_IN|APOL_INFOFLOW_OUT) +#define APOL_INFOFLOW_EITHER 0x04 +%{ + typedef struct apol_infoflow { + apol_infoflow_graph_t *g; + apol_vector_t *v; + } apol_infoflow_t; + apol_infoflow_t *apol_infoflow_create() { + return calloc(1, sizeof(apol_infoflow_t)); + } + void apol_infoflow_destroy(apol_infoflow_t **in) { + if (!in || !(*in)) { + return; + } + free(*in); /* NOTE: does not free contents intentionally */ + *in = NULL; + } +%} +typedef struct apol_infoflow {} apol_infoflow_t; +%extend apol_infoflow_t { + apol_infoflow_t() { + BEGIN_EXCEPTION + SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_infoflow_t objects"); + END_EXCEPTION + fail: + return NULL; + }; + ~apol_infoflow_t() { + apol_infoflow_destroy(&self); + }; + %newobject extract_graph(); + apol_infoflow_graph_t *extract_graph() { + apol_infoflow_graph_t *g = self->g; + self->g = NULL; + return g; + }; + %newobject extract_result_vector(); + apol_vector_t *extract_result_vector() { + apol_vector_t *v = self->v; + self->v = NULL; + return v; + }; +}; +typedef struct apol_infoflow_analysis {} apol_infoflow_analysis_t; +%extend apol_infoflow_analysis_t { + apol_infoflow_analysis_t() { + apol_infoflow_analysis_t *aia; + BEGIN_EXCEPTION + aia = apol_infoflow_analysis_create(); + if (!aia) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return aia; + }; + ~apol_infoflow_analysis_t() { + apol_infoflow_analysis_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_infoflow_t *run(apol_policy_t *p) { + apol_infoflow_t *ai = NULL; + BEGIN_EXCEPTION + ai = apol_infoflow_create(); + if (!ai) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + if (apol_infoflow_analysis_do(p, self, &ai->v, &ai->g)) { + SWIG_exception(SWIG_RuntimeError, "Could not run information flow analysis"); + } + END_EXCEPTION + return ai; + fail: + apol_vector_destroy(&ai->v); + apol_infoflow_graph_destroy(&ai->g); + apol_infoflow_destroy(&ai); + return NULL; + }; + void set_mode(apol_policy_t *p, int mode) { + BEGIN_EXCEPTION + if (apol_infoflow_analysis_set_mode(p, self, (unsigned int)mode)) { + SWIG_exception(SWIG_RuntimeError, "Could not set mode for information flow analysis"); + } + END_EXCEPTION + fail: + return; + }; + void set_dir(apol_policy_t *p, int direction) { + BEGIN_EXCEPTION + if (apol_infoflow_analysis_set_dir(p, self, (unsigned int)direction)) { + SWIG_exception(SWIG_RuntimeError, "Could not set direction for information flow analysis"); + END_EXCEPTION + } + fail: + return; + }; + void set_type(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_infoflow_analysis_set_type(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not set type for information flow analysis"); + } + END_EXCEPTION + fail: + return; + }; + void append_intermediate(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_infoflow_analysis_append_intermediate(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not append intermediate type for information flow analysis"); + } + END_EXCEPTION + fail: + return; + }; + void append_class_perm(apol_policy_t *p, char *class_name, char *perm_name) { + BEGIN_EXCEPTION + if (apol_infoflow_analysis_append_class_perm(p, self, class_name, perm_name)) { + SWIG_exception(SWIG_RuntimeError, "Could not append class and permission for information flow analysis"); + } + END_EXCEPTION + fail: + return; + }; + void set_min_weight(apol_policy_t *p, int weight) { + apol_infoflow_analysis_set_min_weight(p, self, weight); + }; + void set_result_regex(apol_policy_t *p, char *regex) { + BEGIN_EXCEPTION + if (apol_infoflow_analysis_set_result_regex(p, self, regex)) { + SWIG_exception(SWIG_RuntimeError, "Could not set result regular expression for information flow analysis"); + } + END_EXCEPTION + fail: + return; + }; +}; +typedef struct apol_infoflow_graph {} apol_infoflow_graph_t; +%extend apol_infoflow_graph_t { + apol_infoflow_graph_t() { + BEGIN_EXCEPTION + SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_infoflow_graph_t objects"); + END_EXCEPTION + fail: + return NULL; + }; + ~apol_infoflow_graph_t() { + apol_infoflow_graph_destroy(&self); + }; + %newobject do_more(apol_policy_t*, char*); + apol_vector_t *do_more(apol_policy_t *p, char *type) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_infoflow_analysis_do_more(p, self, type, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not do more analysis of information flow graph"); + } + END_EXCEPTION + fail: + return v; + }; + void trans_further_prepare(apol_policy_t *p, char *start_type, char *end_type) { + BEGIN_EXCEPTION + if (apol_infoflow_analysis_trans_further_prepare(p, self, start_type, end_type)) { + SWIG_exception(SWIG_MemoryError, "Error preparing graph for further information flow analysis"); + } + END_EXCEPTION + fail: + return; + }; + apol_vector_t *trans_further_next(apol_policy_t *p, apol_vector_t *v) { + apol_vector_t *retval = NULL; + BEGIN_EXCEPTION + if (apol_infoflow_analysis_trans_further_next(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run further analysis"); + } + END_EXCEPTION + retval = v; + fail: + return retval; + }; +}; +typedef struct apol_infoflow_result {} apol_infoflow_result_t; +%extend apol_infoflow_result_t { + apol_infoflow_result_t() { + BEGIN_EXCEPTION + SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_infoflow_result_t objects"); + END_EXCEPTION + fail: + return NULL; + }; + ~apol_infoflow_result_t() { + /* no op - vector will destroy */ + return; + }; + int get_dir() { + return (int)apol_infoflow_result_get_dir(self); + }; + const qpol_type_t *get_start_type() { + return apol_infoflow_result_get_start_type(self); + }; + const qpol_type_t *get_end_type() { + return apol_infoflow_result_get_end_type(self); + }; + int get_length() { + return (int) apol_infoflow_result_get_length(self); + } + const apol_vector_t *get_steps() { + return apol_infoflow_result_get_steps(self); + }; +}; +%inline %{ + apol_infoflow_result_t *apol_infoflow_result_from_void(void *x) { + return (apol_infoflow_result_t*)x; + }; +%} +typedef struct apol_infoflow_step {} apol_infoflow_step_t; +%extend apol_infoflow_step_t { + apol_infoflow_step_t() { + BEGIN_EXCEPTION + SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_infoflow_step_t objects"); + END_EXCEPTION + fail: + return NULL; + }; + ~apol_infoflow_step_t() { + /* no op */ + return; + }; + const qpol_type_t *get_start_type() { + return apol_infoflow_step_get_start_type(self); + }; + const qpol_type_t *get_end_type() { + return apol_infoflow_step_get_end_type(self); + }; + int get_weight() { + return apol_infoflow_step_get_weight(self); + }; + const apol_vector_t *get_rules() { + return apol_infoflow_step_get_rules(self); + }; +}; +%inline %{ + apol_infoflow_step_t *apol_infoflow_step_from_void(void *x) { + return (apol_infoflow_step_t*)x; + }; +%} + +/* apol relabel analysis */ +#define APOL_RELABEL_DIR_TO 0x01 +#define APOL_RELABEL_DIR_FROM 0x02 +#define APOL_RELABEL_DIR_BOTH (APOL_RELABEL_DIR_TO|APOL_RELABEL_DIR_FROM) +#define APOL_RELABEL_DIR_SUBJECT 0x04 +typedef struct apol_relabel_analysis {} apol_relabel_analysis_t; +%extend apol_relabel_analysis_t { + apol_relabel_analysis_t() { + apol_relabel_analysis_t *ara; + BEGIN_EXCEPTION + ara = apol_relabel_analysis_create(); + if (!ara) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return ara; + }; + ~apol_relabel_analysis_t() { + apol_relabel_analysis_destroy(&self); + }; + %newobject run(apol_policy_t*); + apol_vector_t *run(apol_policy_t *p) { + apol_vector_t *v; + BEGIN_EXCEPTION + if (apol_relabel_analysis_do(p, self, &v)) { + SWIG_exception(SWIG_RuntimeError, "Could not run relabel analysis"); + } + END_EXCEPTION + fail: + return v; + }; + void set_dir(apol_policy_t *p, int direction) { + BEGIN_EXCEPTION + if (apol_relabel_analysis_set_dir(p, self, (unsigned int)direction)) { + SWIG_exception(SWIG_RuntimeError, "Could not set direction for relabel analysis"); + } + END_EXCEPTION + fail: + return; + }; + void set_type(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_relabel_analysis_set_type(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not set type for relabel analysis"); + } + END_EXCEPTION + fail: + return; + }; + void append_class(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_relabel_analysis_append_class(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not append class to relabel analysis"); + } + END_EXCEPTION + fail: + return; + }; + void append_subject(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_relabel_analysis_append_subject(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not append subject to relabel analysis"); + } + END_EXCEPTION + fail: + return; + }; + void set_result_regex(apol_policy_t *p, char *regex) { + BEGIN_EXCEPTION + if (apol_relabel_analysis_set_result_regex(p, self, regex)) { + SWIG_exception(SWIG_RuntimeError, "Could not set result regular expression for relabel analysis"); + } + END_EXCEPTION + fail: + return; + }; +}; +typedef struct apol_relabel_result {} apol_relabel_result_t; +%extend apol_relabel_result_t { + apol_relabel_result_t() { + BEGIN_EXCEPTION + SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_relabel_result_t objects"); + END_EXCEPTION + fail: + return NULL; + }; + ~apol_relabel_result_t() { + /* no op - vector will destroy */ + return; + }; + const apol_vector_t *get_to() { + return apol_relabel_result_get_to(self); + }; + const apol_vector_t *get_from() { + return apol_relabel_result_get_from(self); + }; + const apol_vector_t *get_both() { + return apol_relabel_result_get_both(self); + }; + const qpol_type_t *get_result_type() { + return apol_relabel_result_get_result_type(self); + }; +}; +%inline %{ + apol_relabel_result_t *apol_relabel_result_from_void(void *x) { + return (apol_relabel_result_t*)x; + }; +%} +typedef struct apol_relabel_result_pair {} apol_relabel_result_pair_t; +%extend apol_relabel_result_pair_t { + apol_relabel_result_pair_t() { + BEGIN_EXCEPTION + SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_relabel_result_pair_t objects"); + END_EXCEPTION + fail: + return NULL; + }; + ~apol_relabel_result_pair_t() { + /* no op - owned and free()'d by apol_relabel_result_t */ + return; + }; + const qpol_avrule_t *get_ruleA() { + return apol_relabel_result_pair_get_ruleA(self); + }; + const qpol_avrule_t *get_ruleB() { + return apol_relabel_result_pair_get_ruleB(self); + }; + const qpol_type_t *get_intermediate_type() { + return apol_relabel_result_pair_get_intermediate_type(self); + }; +}; +%inline %{ + apol_relabel_result_pair_t *apol_relabel_result_pair_from_void(void *x) { + return (apol_relabel_result_pair_t*)x; + }; +%} + +/* apol type relation analysis */ +#define APOL_TYPES_RELATION_COMMON_ATTRIBS 0x0001 +#define APOL_TYPES_RELATION_COMMON_ROLES 0x0002 +#define APOL_TYPES_RELATION_COMMON_USERS 0x0004 +#define APOL_TYPES_RELATION_SIMILAR_ACCESS 0x0010 +#define APOL_TYPES_RELATION_DISSIMILAR_ACCESS 0x0020 +#define APOL_TYPES_RELATION_ALLOW_RULES 0x0100 +#define APOL_TYPES_RELATION_TYPE_RULES 0x0200 +#define APOL_TYPES_RELATION_DOMAIN_TRANS_AB 0x0400 +#define APOL_TYPES_RELATION_DOMAIN_TRANS_BA 0x0800 +#define APOL_TYPES_RELATION_DIRECT_FLOW 0x1000 +#define APOL_TYPES_RELATION_TRANS_FLOW_AB 0x4000 +#define APOL_TYPES_RELATION_TRANS_FLOW_BA 0x8000 +typedef struct apol_types_relation_analysis {} apol_types_relation_analysis_t; +%extend apol_types_relation_analysis_t { + apol_types_relation_analysis_t() { + apol_types_relation_analysis_t *atr; + BEGIN_EXCEPTION + atr = apol_types_relation_analysis_create(); + if (!atr) { + SWIG_exception(SWIG_MemoryError, "Out of memory"); + } + END_EXCEPTION + fail: + return atr; + }; + ~apol_types_relation_analysis_t() { + apol_types_relation_analysis_destroy(&self); + } + %newobject run(apol_policy_t*); + apol_types_relation_result_t *run(apol_policy_t *p) { + apol_types_relation_result_t *res; + BEGIN_EXCEPTION + if (apol_types_relation_analysis_do(p, self, &res)) { + SWIG_exception(SWIG_RuntimeError, "Could not run types relation analysis"); + } + END_EXCEPTION + fail: + return res; + }; + void set_first_type(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_types_relation_analysis_set_first_type(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not set first type for types relation analysis"); + } + END_EXCEPTION + fail: + return; + }; + void set_other_type(apol_policy_t *p, char *name) { + BEGIN_EXCEPTION + if (apol_types_relation_analysis_set_other_type(p, self, name)) { + SWIG_exception(SWIG_RuntimeError, "Could not set other type for types relation analysis"); + } + END_EXCEPTION + fail: + return; + }; + void set_analyses(apol_policy_t *p, int analyses) { + BEGIN_EXCEPTION + if (apol_types_relation_analysis_set_analyses(p, self, (unsigned int)analyses)) { + SWIG_exception(SWIG_RuntimeError, "Could not set analyses to run for types relation analysis"); + } + END_EXCEPTION + fail: + return; + }; +}; +typedef struct apol_types_relation_result {} apol_types_relation_result_t; +%extend apol_types_relation_result_t { + apol_types_relation_result_t() { + BEGIN_EXCEPTION + SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_types_relation_result_t objects"); + END_EXCEPTION + fail: + return NULL; + }; + ~apol_types_relation_result_t() { + apol_types_relation_result_destroy(&self); + }; + const apol_vector_t *get_attributes() { + return apol_types_relation_result_get_attributes(self); + }; + const apol_vector_t *get_roles() { + return apol_types_relation_result_get_roles(self); + }; + const apol_vector_t *get_users() { + return apol_types_relation_result_get_users(self); + }; + const apol_vector_t *get_similar_first() { + return apol_types_relation_result_get_similar_first(self); + }; + const apol_vector_t *get_similar_other() { + return apol_types_relation_result_get_similar_other(self); + }; + const apol_vector_t *get_dissimilar_first() { + return apol_types_relation_result_get_dissimilar_first(self); + }; + const apol_vector_t *get_dissimilar_other() { + return apol_types_relation_result_get_dissimilar_other(self); + }; + const apol_vector_t *get_allowrules() { + return apol_types_relation_result_get_allowrules(self); + }; + const apol_vector_t *get_typerules() { + return apol_types_relation_result_get_typerules(self); + }; + const apol_vector_t *get_directflows() { + return apol_types_relation_result_get_directflows(self); + }; + const apol_vector_t *get_transflowsAB() { + return apol_types_relation_result_get_transflowsAB(self); + }; + const apol_vector_t *get_transflowsBA() { + return apol_types_relation_result_get_transflowsBA(self); + }; + const apol_vector_t*get_domainsAB() { + return apol_types_relation_result_get_domainsAB(self); + }; + const apol_vector_t*get_domainsBA() { + return apol_types_relation_result_get_domainsBA(self); + }; +}; +typedef struct apol_types_relation_access {} apol_types_relation_access_t; +%extend apol_types_relation_access_t { + apol_types_relation_access_t() { + BEGIN_EXCEPTION + SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_types_relation_access_t objects"); + END_EXCEPTION + fail: + return NULL; + }; + ~apol_types_relation_access_t() { + /* no op - vector will destroy */ + return; + }; + const qpol_type_t *get_type() { + return apol_types_relation_access_get_type(self); + }; + const apol_vector_t *get_rules() { + return apol_types_relation_access_get_rules(self); + }; +}; +%inline %{ + apol_types_relation_access_t *apol_types_relation_access_from_void(void *x) { + return (apol_types_relation_access_t*)x; + }; +%} +// vim:ft=c diff --git a/libapol/swig/java/MANIFEST.MF.in b/libapol/swig/java/MANIFEST.MF.in new file mode 100644 index 0000000..2c0eee8 --- /dev/null +++ b/libapol/swig/java/MANIFEST.MF.in @@ -0,0 +1,12 @@ +Manifest-Version: 1.0 + +Name: com/tresys/setools/ +Specification-Title: "SETools Java Libraries" +Specification-Version: "@VERSION@" +Specification-Vendor: "Tresys Technology" +Implementation-Title: "com.tresys.setools.apol" +Implementation-Version: "@libapol_version@" +Implementation-Vendor: "Tresys Technology" +Extension-List: qpol +qpol-Extension-Name: com.tresys.setools.qpol +qpol-Implementation-Version: @libqpel_version@ diff --git a/libapol/swig/java/Makefile.am b/libapol/swig/java/Makefile.am new file mode 100644 index 0000000..ea087b8 --- /dev/null +++ b/libapol/swig/java/Makefile.am @@ -0,0 +1,118 @@ +wrappedso_DATA = libjapol.so.@libapol_version@ +wrappedso_SONAME = @libapol_jswig_soname@ +short_name = libjapol.so +wrappedsodir = $(libdir) + +package_name = com.tresys.setools.apol +package_dir = $(dir $(subst .,/,$(package_name)))apol + +wrappedjar_DATA = apol.jar +wrappedjardir = $(setoolsdir) + +dist_noinst_DATA = $(srcdir)/../apol.i +BUILT_SOURCES = apol_wrap.c \ + apol_attr_query_t.java \ + apol_avrule_query_t.java \ + apol_bool_query_t.java \ + apol_cat_query_t.java \ + apol_class_query_t.java \ + apol_common_query_t.java \ + apol_cond_query_t.java \ + apolConstants.java \ + apol_constraint_query_t.java \ + apol_context_t.java \ + apol_domain_trans_analysis_t.java \ + apol_domain_trans_result_t.java \ + apol_fs_use_query_t.java \ + apol_genfscon_query_t.java \ + apol_infoflow_analysis_t.java \ + apol_infoflow_graph_t.java \ + apol_infoflow_result_t.java \ + apol_infoflow_step_t.java \ + apol_infoflow_t.java \ + apol_ip_t.java \ + apol_isid_query_t.java \ + apol.java \ + apolJNI.java \ + apol_level_query_t.java \ + apol_mls_level_t.java \ + apol_mls_range_t.java \ + apol_netifcon_query_t.java \ + apol_nodecon_query_t.java \ + apol_perm_query_t.java \ + apol_policy_path_t.java \ + apol_policy_path_type_e.java \ + apol_policy_t.java \ + apol_portcon_query_t.java \ + apol_range_trans_query_t.java \ + apol_relabel_analysis_t.java \ + apol_relabel_result_pair_t.java \ + apol_relabel_result_t.java \ + apol_role_allow_query_t.java \ + apol_role_query_t.java \ + apol_role_trans_query_t.java \ + apol_string_vector_t.java \ + apol_terule_query_t.java \ + apol_type_query_t.java \ + apol_types_relation_access_t.java \ + apol_types_relation_analysis_t.java \ + apol_types_relation_result_t.java \ + apol_user_query_t.java \ + apol_validatetrans_query_t.java \ + apol_vector_t.java \ + SWIGTYPE_p_unsigned_int.java \ + SWIGTYPE_p_void.java + +AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \ + @QPOL_CFLAGS@ -I$(top_builddir) -fpic \ + -I$(top_srcdir)/libapol/include +AM_JFLAGS = @DEBUGJFLAGS@ @WARNJFLAGS@ \ + -classpath $(top_builddir)/libqpol/swig/java/qpol.jar +AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \ + @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ +DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \ + $(top_builddir)/libapol/src/libapol.so + +$(firstword $(BUILT_SOURCES)): $(dist_noinst_DATA) $(DEPENDENCIES) + $(SWIG) $(SWIG_JAVA_OPT) -package $(package_name) -o $@ -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/swig $< + +$(wordlist 2,$(words $(BUILT_SOURCES)), $(BUILT_SOURCES)): $(firstword $(BUILT_SOURCES)) + +$(wrappedso_DATA): $(filter %.c, $(BUILT_SOURCES)) + $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_JAVA_CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME) + $(LN_S) -f $@ $(wrappedso_SONAME) + $(LN_S) -f $@ $(short_name) + +# Intentionally do not include SWIGTYPE_p_void.java below so that the +# Java compiler uses the one created in package +# com.tresys.setools.qpol instead of the one from package +# com.tresys.setools.apol. +java_files = $(filter-out SWIGTYPE_p_void.java, $(filter %.java, $(BUILT_SOURCES))) + +classes = $(patsubst %.java, $(package_dir)/%.class, $(java_files)) + +# Because the Java compiler can generate multiple class files from the +# same source .java file, putting all of the classes below will result +# in repeated invocations of javac. Therefore, an alternative is to +# just depend upon the first class file, and let the Java compiler +# create the rest of them. +$(firstword $(classes)): $(java_files) + $(JAVAC) $(AM_JFLAGS) $(JAVAFLAGS) -d . $^ + +$(wordlist 2,$(words $(classes)),$(classes)): $(firstword $(classes)) + +$(wrappedjar_DATA): MANIFEST.MF + +$(wrappedjar_DATA): $(classes) + $(JAR) cfm $@ MANIFEST.MF $^ + +install-data-hook: + cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(wrappedso_SONAME) + cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(short_name) + $(mkdir_p) $(DESTDIR)$(javadir) && cd $(DESTDIR)$(javadir) && $(LN_S) -f $(wrappedjardir)/$(wrappedjar_DATA) + +uninstall-local: + -rm -rf $(DESTDIR)$(wrappedsodir)/$(wrappedso_SONAME) $(DESTDIR)$(wrappedsodir)/$(short_name) + -rm -f $(DESTDIR)$(javadir)/$(wrappedjar_DATA) + +MOSTLYCLEANFILES = $(BUILT_SOURCES) $(classes) $(wrappedso_DATA) $(wrappedjar_DATA) $(wrappedso_SONAME) $(short_name) diff --git a/libapol/swig/python/Makefile.am b/libapol/swig/python/Makefile.am new file mode 100644 index 0000000..649feff --- /dev/null +++ b/libapol/swig/python/Makefile.am @@ -0,0 +1,36 @@ +wrappedso_DATA = _apol.so.@libapol_version@ +wrappedso_SONAME = @libapol_pyswig_soname@ +wrappedsodir = $(pkgpyexecdir) + +wrappedpy_DATA = apol.py +wrappedpydir = $(pkgpyexecdir) + +dist_noinst_DATA = $(srcdir)/../apol.i +BUILT_SOURCES = apol_wrap.c + +AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \ + @QPOL_CFLAGS@ -I$(top_builddir) -fpic \ + -I$(top_srcdir)/libapol/include +AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \ + @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ +DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \ + $(top_builddir)/libapol/src/libapol.so + +$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES) + $(SWIG) $(SWIG_PYTHON_OPT) -o $@ -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/swig $< + +$(wrappedso_DATA): $(BUILT_SOURCES) + $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_PYTHON_CPPFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME) + $(LN_S) -f $@ $(wrappedso_SONAME) + $(LN_S) -f $@ _apol.so + +$(wrappedpy_DATA): $(BUILT_SOURCES) + +install-data-hook: + cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(wrappedso_SONAME) + cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) _apol.so + +uninstall-local: + -rm -rf $(DESTDIR)$(wrappedsodir)/$(wrappedso_SONAME) $(DESTDIR)$(wrappedsodir)/_apol.so + +MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) $(wrappedpy_DATA) $(wrappedso_SONAME) _apol.so apol.pyc diff --git a/libapol/swig/tcl/Makefile.am b/libapol/swig/tcl/Makefile.am new file mode 100644 index 0000000..40a206a --- /dev/null +++ b/libapol/swig/tcl/Makefile.am @@ -0,0 +1,36 @@ +wrappedso_DATA = libtapol.so.@libapol_version@ +wrappedso_SONAME = @libapol_tswig_soname@ +short_name = libtapol.so +wrappedsodir = $(libdir)/setools/apol + +package_SCRIPTS = pkgIndex.tcl +packagedir = $(wrappedsodir) + +dist_noinst_DATA = $(srcdir)/../apol.i +BUILT_SOURCES = apol_wrap.c + +AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \ + @QPOL_CFLAGS@ -I$(top_builddir) -fpic \ + -I$(top_srcdir)/libapol/include +AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \ + @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ +DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \ + $(top_builddir)/libapol/src/libapol.so + +$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES) + $(SWIG) $(SWIG_TCL_OPT) -pkgversion @libapol_version@ -o $@ -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/swig $< + +$(wrappedso_DATA): $(BUILT_SOURCES) + $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_TCL_CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME) + $(LN_S) -f $@ $(wrappedso_SONAME) + $(LN_S) -f $@ $(short_name) + +$(package_SCRIPTS): $(wrappedso_DATA) + echo "pkg_mkIndex . $^" | LD_LIBRARY_PATH=$(top_builddir)/libqpol/src:$(top_builddir)/libapol/src $(TCLSH_PROG) + chmod 644 $@ + $(mkdir_p) apol + cp $(wrappedso_DATA) $@ apol + +MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) $(wrappedso_SONAME) $(short_name) $(package_DATA) apol/$(wrappedso_DATA) apol/$(package_SCRIPTS) + +CLEANFILES = $(package_SCRIPTS) diff --git a/libapol/tests/Makefile.am b/libapol/tests/Makefile.am new file mode 100644 index 0000000..ed5005e --- /dev/null +++ b/libapol/tests/Makefile.am @@ -0,0 +1,23 @@ +TESTS = libapol-tests +check_PROGRAMS = libapol-tests + +libapol_tests_SOURCES = \ + avrule-tests.c avrule-tests.h \ + dta-tests.c dta-tests.h \ + infoflow-tests.c infoflow-tests.h \ + policy-21-tests.c policy-21-tests.h \ + role-tests.c role-tests.h \ + terule-tests.c terule-tests.h \ + user-tests.c user-tests.h \ + constrain-tests.c constrain-tests.h \ + ../../libqpol/src/queue.c ../../libqpol/src/queue.h \ + libapol-tests.c + +AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \ + @QPOL_CFLAGS@ @APOL_CFLAGS@ -DTOP_SRCDIR="\"$(top_srcdir)\"" + +AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ + +LDADD = @SELINUX_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @CUNIT_LIB_FLAG@ + +libapol_tests_DEPENDENCIES = ../src/libapol.so diff --git a/libapol/tests/avrule-tests.c b/libapol/tests/avrule-tests.c new file mode 100644 index 0000000..f86bbe2 --- /dev/null +++ b/libapol/tests/avrule-tests.c @@ -0,0 +1,161 @@ +/** + * @file + * + * Test the AV rule queries, both semantic and syntactic searches. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <CUnit/CUnit.h> +#include <apol/avrule-query.h> +#include <apol/policy.h> +#include <apol/policy-path.h> +#include <qpol/policy_extend.h> +#include <stdbool.h> + +#define BIN_POLICY TEST_POLICIES "/setools-3.3/rules/rules-mls.21" +#define SOURCE_POLICY TEST_POLICIES "/setools-3.3/rules/rules-mls.conf" + +static apol_policy_t *bp = NULL; +static apol_policy_t *sp = NULL; + +static void avrule_basic_syn(void) +{ + apol_avrule_query_t *aq = apol_avrule_query_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(aq); + + int retval; + retval = apol_avrule_query_set_rules(sp, aq, QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + apol_vector_t *v = NULL; + retval = apol_syn_avrule_get_by_query(sp, aq, &v); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_PTR_NOT_NULL(v); + + size_t num_auditallows = 0, num_dontaudits = 0; + + qpol_policy_t *q = apol_policy_get_qpol(sp); + size_t i; + for (i = 0; i < apol_vector_get_size(v); i++) { + const qpol_syn_avrule_t *syn = (const qpol_syn_avrule_t *)apol_vector_get_element(v, i); + uint32_t rule_type; + retval = qpol_syn_avrule_get_rule_type(q, syn, &rule_type); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + CU_ASSERT(rule_type == QPOL_RULE_AUDITALLOW || rule_type == QPOL_RULE_DONTAUDIT); + if (rule_type == QPOL_RULE_AUDITALLOW) { + num_auditallows++; + } else if (rule_type == QPOL_RULE_DONTAUDIT) { + num_dontaudits++; + } + } + CU_ASSERT(num_auditallows == 4 && num_dontaudits == 1); + apol_vector_destroy(&v); + + retval = apol_avrule_query_append_class(sp, aq, "unknown class"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + retval = apol_syn_avrule_get_by_query(sp, aq, &v); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0); + apol_vector_destroy(&v); + apol_avrule_query_destroy(&aq); +} + +static void avrule_default(void) +{ + apol_avrule_query_t *aq = apol_avrule_query_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(aq); + + int retval; + qpol_policy_t *sq = apol_policy_get_qpol(sp); + + apol_vector_t *v = NULL; + + retval = apol_avrule_get_by_query(sp, aq, &v); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_PTR_NOT_NULL(v); + CU_ASSERT(apol_vector_get_size(v) == 396); + apol_vector_destroy(&v); + + qpol_policy_rebuild(sq, QPOL_POLICY_OPTION_NO_NEVERALLOWS); + retval = apol_avrule_get_by_query(sp, aq, &v); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_PTR_NOT_NULL(v); + CU_ASSERT(apol_vector_get_size(v) == 21); + apol_vector_destroy(&v); + + retval = apol_avrule_get_by_query(bp, aq, &v); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_PTR_NOT_NULL(v); + CU_ASSERT(apol_vector_get_size(v) == 21); + apol_vector_destroy(&v); + + apol_avrule_query_destroy(&aq); +} + +CU_TestInfo avrule_tests[] = { + {"basic syntactic search", avrule_basic_syn} + , + {"default query", avrule_default} + , + CU_TEST_INFO_NULL +}; + +int avrule_init() +{ + apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, BIN_POLICY, NULL); + if (ppath == NULL) { + return 1; + } + + if ((bp = apol_policy_create_from_policy_path(ppath, 0, NULL, NULL)) == NULL) { + apol_policy_path_destroy(&ppath); + return 1; + } + apol_policy_path_destroy(&ppath); + + ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, SOURCE_POLICY, NULL); + if (ppath == NULL) { + return 1; + } + + if ((sp = apol_policy_create_from_policy_path(ppath, 0, NULL, NULL)) == NULL) { + apol_policy_path_destroy(&ppath); + return 1; + } + apol_policy_path_destroy(&ppath); + + if (qpol_policy_build_syn_rule_table(apol_policy_get_qpol(sp)) != 0) { + return 1; + } + + return 0; +} + +int avrule_cleanup() +{ + apol_policy_destroy(&bp); + apol_policy_destroy(&sp); + return 0; +} diff --git a/libapol/tests/avrule-tests.h b/libapol/tests/avrule-tests.h new file mode 100644 index 0000000..f56ab25 --- /dev/null +++ b/libapol/tests/avrule-tests.h @@ -0,0 +1,35 @@ +/** + * @file + * + * Declarations for libapol avrule query tests. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRULE_TESTS_H +#define AVRULE_TESTS_H + +#include <CUnit/CUnit.h> + +extern CU_TestInfo avrule_tests[]; +extern int avrule_init(); +extern int avrule_cleanup(); + +#endif diff --git a/libapol/tests/constrain-tests.c b/libapol/tests/constrain-tests.c new file mode 100644 index 0000000..3133b5d --- /dev/null +++ b/libapol/tests/constrain-tests.c @@ -0,0 +1,523 @@ +/** + * @file + * + * Test the information flow analysis code. + * + * + * Copyright (C) 2010 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <config.h> + +#include <CUnit/CUnit.h> +#include <apol/perm-map.h> +#include <apol/policy.h> +#include <apol/policy-path.h> +#include <stdbool.h> +#include <string.h> +#include <apol/constraint-query.h> +#include <sepol/policydb/policydb.h> +#include <sepol/policydb/constraint.h> +#include <libqpol/src/queue.h> + +#define CONSTR_SOURCE TEST_POLICIES "/setools-3.3/apol/constrain_test_policy.conf" +#define CONSTR_BINARY TEST_POLICIES "/setools-3.3/apol/constrain_test_policy.21" +// Glob won't work, but this gives the idea of where we are trying to go +#define CONSTR_MODULAR TEST_POLICIES "/setools-3.1/modules/*.pp" + +//#define DEBUGTRACE 1 + + +/* General concepts: The constraints are stored in the policy by class, + * that is, the list of classes stored in the policy has attached to it + * whatever constraints affect that class. + * The "policy_iter" iterator is a structure which contains a pointer to the + * list of classes from the loaded policy, and another pointer to the list of + * constraints associated with the current class. This latter pointer is + * traversed to its end, at which point the class pointer is updated, and the + * new class' list of constraints is put in its place. The switch from one + * class to the next is done behind the scenes by the iterator. Thus each time + * a new item is retrieved from policy_iter, it needs to have all info (class, + * permissions, expression) extracted from it. + * + * The input file must be a known file. The class and permissions are used as + * a key by this test routine to determine what the expected expression will + * be. Thus, if the input file is modified, this test becomes invalid. The file + * (defined above) resides in the 'testing-policies' repository. + * + * The statements validatetrans and mlsvalidatetrans, although similar to + * constrain and mlsconstrain, are not considered here. + * + */ + +// Define data for expected policy. This is a hack, but what I could think of +// on short notice. + +// Similar to struct constraint_expr from sepol/policydb/constraint.h +// but want char * list of names, not internal representations. +typedef struct local_expr { + uint32_t expr_type; + uint32_t attr; + uint32_t op; + size_t name_count; + char **namelist; +} local_expr_t; + +typedef struct constrain_test_list { + char **class; + char **permissions; // Must end with NULL + int test_found; + int expr_count; + local_expr_t **expr_list; +} constrain_test_list_t; + +// TODO Clean up memory leaks -- all iterators need to be destroyed, check other stuff + + +char *class0 = "file"; +char *perm0[] = { "create", "relabelto", NULL }; +local_expr_t expr00 = { CEXPR_ATTR, CEXPR_L2H2, CEXPR_EQ, 0, NULL }; +local_expr_t *expr0[] = { &expr00, NULL }; + +char *class1 = "lnk_file"; +char *perm1[10] = { "create", "relabelto", NULL }; +local_expr_t expr10 = { CEXPR_ATTR, CEXPR_L2H2, CEXPR_NEQ, 0, NULL }; +local_expr_t *expr1[] = { &expr10, NULL }; + +// This test (test 2) is not expected to be matched +char *class2 = "fifo_file"; +char *perm2[] = { "create", "relabelto", NULL }; +local_expr_t expr20 = { CEXPR_ATTR, CEXPR_L2H2, CEXPR_DOM, 0, NULL }; +local_expr_t *expr2[] = { &expr20, NULL }; + +char *class3 = "node"; +char *perm3[] = { "udp_send", NULL }; +local_expr_t expr30 = { CEXPR_ATTR, CEXPR_L1L2, CEXPR_DOM, 0, NULL }; +local_expr_t expr31 = { CEXPR_ATTR, CEXPR_L1H2, CEXPR_DOMBY, 0, NULL }; +local_expr_t expr32 = { CEXPR_AND, 0, 0, 0, NULL }; +local_expr_t *expr3[] = { &expr30, &expr31, &expr32, NULL }; + +char *class4 = "netif"; +char *perm4[] = { "tcp_send", NULL }; +local_expr_t expr40 = { CEXPR_ATTR, CEXPR_L1L2, CEXPR_DOM, 0, NULL }; +local_expr_t expr41 = { CEXPR_ATTR, CEXPR_L1H2, CEXPR_DOMBY, 0, NULL }; +local_expr_t expr42 = { CEXPR_OR, 0, 0, 0, NULL }; +local_expr_t *expr4[] = { &expr40, &expr41, &expr42, NULL }; + +char *class5 = "dir"; +char *perm5[] = { "read", NULL }; +char *name50[] = { "sysadm_t", "secadm_t", NULL }; +local_expr_t expr50 = { CEXPR_NAMES, CEXPR_TYPE, CEXPR_EQ, 2, name50 }; +local_expr_t *expr5[] = { &expr50, NULL }; + +constrain_test_list_t test_list[] = { + { &class0, perm0, 0, 1, expr0 }, + { &class1, perm1, 0, 1, expr1 }, + { &class2, perm2, 0, 1, expr2 }, + { &class3, perm3, 0, 3, expr3 }, + { &class4, perm4, 0, 3, expr4 }, + { &class5, perm5, 0, 3, expr5 } +}; + +typedef struct compare_perm_str { + int list_length; + int list_found; + int q_elements_compared; + char **list; +} compare_perm_str_t; + +typedef struct compare_expr_str { + int list_length; + int list_found; + local_expr_t **list; +} compare_expr_str_t; + + +static apol_policy_t *ps = NULL; // Source policy +static apol_policy_t *pb = NULL; // Binary policy +static apol_policy_t *pm = NULL; // Modular policy + + +// Prototypes if needed +static int compare_item_to_list(void *e, void *v); + + + + +static int doprintstr (queue_element_t e, void *p) +{ + char *s = (char *)e; + // Second arg is not used + + printf ("%s ", s); + return 0; +} + +static int compare_expr_list(qpol_policy_t *q, qpol_iterator_t *expr_iter, int expr_count, local_expr_t **le) +{ + const qpol_constraint_expr_node_t *expr; + int sym_type; + int op; + int expr_type; + int i; + int err; + + for (i=0; qpol_iterator_end(expr_iter) == 0; i++, qpol_iterator_next(expr_iter)) + { + expr_type = op = sym_type = 0; + if (i >= expr_count) // Hit the end of the list + return 1; // Not the right list + + err = qpol_iterator_get_item(expr_iter, (void **)&expr); + CU_ASSERT_EQUAL_FATAL(err, 0); + + err = qpol_constraint_expr_node_get_sym_type(q, expr, &sym_type); + CU_ASSERT_EQUAL_FATAL(err, 0); + + err = qpol_constraint_expr_node_get_op(q, expr, &op); + CU_ASSERT_EQUAL_FATAL(err, 0); + + err = qpol_constraint_expr_node_get_expr_type(q, expr, &expr_type); + CU_ASSERT_EQUAL_FATAL(err, 0); + +#ifdef DEBUGTRACE + printf ("Expr compare: Policy:attr:%d, op:%d, expr_type:%d\n", sym_type, op, expr_type); + printf ("Expr compare: Test:attr:%d, op:%d, expr_type:%d\n", le[i]->attr, le[i]->op, le[i]->expr_type); +#endif + if (sym_type != le[i]->attr) + { + return 1; + } + if (op != le[i]->op) + { + return 1; + } + if (expr_type != le[i]->expr_type) + { + return 1; + } + + if (expr_type == CEXPR_NAMES) // Need compare name lists + { + qpol_iterator_t *names_iter=NULL; + size_t name_size=0; + compare_perm_str_t x; + +#ifdef DEBUGTRACE + printf ("Found CEXPR_NAMES expression\n"); +#endif + x.list_length = le[i]->name_count; + x.list = le[i]->namelist; + x.list_found = 0; + x.q_elements_compared = 0; + + err = qpol_constraint_expr_node_get_names_iter (q, expr, &names_iter); + CU_ASSERT_EQUAL_FATAL(err, 0); + + err = qpol_iterator_get_size(names_iter, &name_size); + CU_ASSERT_EQUAL_FATAL(err, 0); + CU_ASSERT_TRUE_FATAL(name_size > 0); + + if (name_size != x.list_length) // Want exact match, + { + qpol_iterator_destroy(&names_iter); + return 1; + } + + for (; qpol_iterator_end(names_iter) == 0; qpol_iterator_next(names_iter)) + { + char *lname = NULL; + + err = qpol_iterator_get_item (names_iter, (void **)&lname); + CU_ASSERT_EQUAL_FATAL(err, 0); + + compare_item_to_list (lname, &x); + free (lname); + } + +#ifdef DEBUGTRACE + printf ("name list length=%d, list_found=%d, q_elements_compared=%d\n", x.list_length, x.list_found, x.q_elements_compared); +#endif + if ((x.list_length != x.list_found) || (x.list_length != x.q_elements_compared)) + return 1; + } + } + return 0; +} + +static int compare_item_to_list(void *e, void *v) +{ + char *pe = (char *)e; + compare_perm_str_t *x = (compare_perm_str_t *)v; + char **permlist = x->list; + char *perm; + + CU_ASSERT_PTR_NOT_NULL(permlist); + CU_ASSERT_PTR_NOT_NULL(pe); + + while ((perm=*permlist++) != NULL) + { +#ifdef DEBUGTRACE + printf ("pe = %s\n", pe); + printf ("perm = %s\n", perm); +#endif + if (strcmp(pe, perm) == 0) + x->list_found++; + } + x->q_elements_compared++; + return 0; +} + +static int compare_perm_list(queue_t perm_q, char **permissions) +{ + compare_perm_str_t x; + + x.list_length = 0; + x.list_found = 0; + x.q_elements_compared = 0; + x.list = permissions; + + while (*permissions++ != NULL) + x.list_length++; + +#ifdef DEBUGTRACE + printf ("list_length = %d\n", x.list_length); +#endif + if (queue_map(perm_q, compare_item_to_list, &x) != 0) + return 1; + +#ifdef DEBUGTRACE + printf ("list length=%d, list_found=%d, q_elements_compared=%d\n", x.list_length, x.list_found, x.q_elements_compared); +#endif + if ((x.list_length != x.list_found) || (x.list_length != x.q_elements_compared)) + return 1; + + return 0; +} + +static void constrain_test(apol_policy_t *ap) +{ + int i; + int err=0; + const char *class_name = NULL; + const char *constrain_type = "?constrain"; + char *perm_list = "No Perms Extracted"; + const qpol_constraint_expr_node_t *expr = NULL; + qpol_iterator_t *policy_iter = NULL; // Iterates over all constraints in a policy + qpol_iterator_t *perm_iter = NULL; // Iterates over permissions in a constraint + qpol_iterator_t *expr_iter = NULL; // Iterates over expression in a constraint + qpol_policy_t *q = apol_policy_get_qpol(ap); + qpol_constraint_t *constraint = NULL; + const qpol_class_t *class; + size_t n_constraints = 0; + size_t counted_constraints = 0; + size_t tests_not_found = 0; + int test_count = sizeof(test_list) / sizeof(constrain_test_list_t); + int tests_matched = 0; + int constrains_matched = 0; + + queue_t perm_q; // holds list of permissions, in case more than one + + err = qpol_policy_get_constraint_iter(q, &policy_iter); + if (err != 0) + { + CU_FAIL("Policy iterator not accessible"); + goto cleanup; + } + err = qpol_iterator_get_size(policy_iter, &n_constraints); + if (err != 0) + { + CU_FAIL("Policy size computation failed"); + goto cleanup; + } + + CU_ASSERT_EQUAL(n_constraints, 7); // Count of constraints split among all classes + + counted_constraints=0; + for (i=0; i<test_count; i++) + { + test_list[i].test_found = 0; + } + + // Iterate through constraints + for (; qpol_iterator_end(policy_iter) == 0; qpol_iterator_next(policy_iter)) + { + counted_constraints++; + /* The qpol_constraint_t that is returned below consists of + * struct qpol_constraint <<<from constraint_query.c + * { + * const qpol_class_t *obj_class; + * constraint_node_t *constr; + * }; + * the qpol_class_t is a pseudonym for class_datum_t from policydb.h + * constraint_node_t is defined in sepol/policydb/constraint.h + */ + err = qpol_iterator_get_item(policy_iter, (void **)&constraint); + CU_ASSERT_EQUAL_FATAL(err, 0); // Should never happen + + err = qpol_constraint_get_class(q, constraint, &class); + CU_ASSERT_EQUAL_FATAL(err, 0); // Should never happen + err = qpol_class_get_name(q, class, &class_name); + CU_ASSERT_EQUAL_FATAL(err, 0); // Should never happen + +#ifdef DEBUGTRACE + printf ("Found class %s\n", class_name); +#endif + // get permission(s) + err = qpol_constraint_get_perm_iter (q, constraint, &perm_iter); + CU_ASSERT_EQUAL_FATAL(err, 0); + + perm_q = queue_create(); + for (; qpol_iterator_end(perm_iter) == 0; qpol_iterator_next(perm_iter)) + { + err = qpol_iterator_get_item(perm_iter, (void **)&perm_list); + CU_ASSERT_EQUAL_FATAL(err,0) + + err = queue_insert (perm_q, perm_list); + CU_ASSERT_EQUAL_FATAL(err,0) + } +#ifdef DEBUGTRACE + printf ("perms: "); + queue_map(perm_q, doprintstr, NULL); + printf ("\n"); +#endif + + // get RPN expressions + err = qpol_constraint_get_expr_iter (q, constraint, &expr_iter); + CU_ASSERT_EQUAL_FATAL(err, 0); + + // At this point, the class, permission list, and expression list (in + // the iterator) have been identified. Based on expected class/permission + // combinations, find one which matches, and note that it was found. + // If not found, count that too. + for (i=0; i<test_count; i++) + { + if (strcmp(*(test_list[i].class), class_name) == 0) + { + if (compare_perm_list(perm_q, test_list[i].permissions) == 0) + { + if (compare_expr_list(q, expr_iter, test_list[i].expr_count, test_list[i].expr_list) == 0) + { + test_list[i].test_found = 1; + constrains_matched++; + break; + } +#ifdef DEBUGTRACE + else + { + printf ("Mismatch comparing expression list\n"); + } +#endif + } +#ifdef DEBUGTRACE + else + { + printf ("Mismatch comparing permission list\n"); + } +#endif + } +#ifdef DEBUGTRACE + else + { + printf ("Mismatch comparing classes %s,%s\n", *(test_list[i].class),class_name); + } +#endif + } + queue_destroy(perm_q); + } + for (i=0; i<test_count; i++) + { + if (test_list[i].test_found == 0) + { + CU_ASSERT_EQUAL(i, 2); + } + else + tests_matched++; + } +#ifdef DEBUGTRACE + printf ("tests_matched: %d, constrains_matched: %d, counted_constraints: %d, n_constraints: %d\n", tests_matched, constrains_matched, counted_constraints, n_constraints); +#endif + CU_ASSERT_EQUAL(tests_matched, 5); + CU_ASSERT_EQUAL(constrains_matched, 5); + CU_ASSERT_EQUAL(counted_constraints, 7); + CU_ASSERT_EQUAL(n_constraints, 7); + + CU_PASS(); + +cleanup: + return; + // close and destroy iterators/policy pointers +} + +static void constrain_source(void) +{ + constrain_test(ps); +} + +static void constrain_binary(void) +{ + constrain_test(pb); +// CU_PASS("Not yet implemented") +} + + +static void constrain_modular(void) +{ + CU_PASS("Not yet implemented") +} + +CU_TestInfo constrain_tests[] = { + {"constrain from source policy", constrain_source}, + {"constrain from binary policy", constrain_binary}, +// {"constrain from modular policy", constrain_modular}, + CU_TEST_INFO_NULL +}; + +int constrain_init() +{ + // Probably should move this to individual tests, just fstat policy to see if it is there! + apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, CONSTR_SOURCE, NULL); + if (ppath == NULL) { + return 1; + } + + if ((ps = apol_policy_create_from_policy_path(ppath, QPOL_POLICY_OPTION_NO_NEVERALLOWS, NULL, NULL)) == NULL) { + apol_policy_path_destroy(&ppath); + return 1; + } + apol_policy_path_destroy(&ppath); + + ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, CONSTR_BINARY, NULL); + if (ppath == NULL) { + return 1; + } + + if ((pb = apol_policy_create_from_policy_path(ppath, QPOL_POLICY_OPTION_NO_NEVERALLOWS, NULL, NULL)) == NULL) { + apol_policy_path_destroy(&ppath); + return 1; + } + apol_policy_path_destroy(&ppath); + + return 0; +} + +int constrain_cleanup() +{ + apol_policy_destroy(&ps); + return 0; +} diff --git a/libapol/tests/constrain-tests.h b/libapol/tests/constrain-tests.h new file mode 100644 index 0000000..e87c020 --- /dev/null +++ b/libapol/tests/constrain-tests.h @@ -0,0 +1,33 @@ +/** + * @file + * + * Declarations for libapol constraint tests. + * + * + * Copyright (C) 2010 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONSTRAIN_TESTS_H +#define CONSTRAIN_TESTS_H + +#include <CUnit/CUnit.h> + +extern CU_TestInfo constrain_tests[]; +extern int constrain_init(); +extern int constrain_cleanup(); + +#endif diff --git a/libapol/tests/dta-tests.c b/libapol/tests/dta-tests.c new file mode 100644 index 0000000..d25fd82 --- /dev/null +++ b/libapol/tests/dta-tests.c @@ -0,0 +1,529 @@ +/** + * @file + * + * Test the new domain transition analysis code introduced in SETools + * 3.3. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <CUnit/CUnit.h> +#include <apol/avrule-query.h> +#include <apol/domain-trans-analysis.h> +#include <apol/policy.h> +#include <apol/policy-path.h> +#include <stdbool.h> +#include <string.h> + +#define POLICY TEST_POLICIES "/setools-3.3/apol/dta_test.policy.conf" + +static apol_policy_t *p = NULL; + +static void dta_forward(void) +{ + apol_policy_reset_domain_trans_table(p); + apol_domain_trans_analysis_t *d = apol_domain_trans_analysis_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(d); + int retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_FORWARD); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_set_start_type(p, d, "tuna_t"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + apol_vector_t *v = NULL; + retval = apol_domain_trans_analysis_do(p, d, &v); + apol_domain_trans_analysis_destroy(&d); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_PTR_NOT_NULL(v); + + qpol_policy_t *q = apol_policy_get_qpol(p); + size_t i; + for (i = 0; i < apol_vector_get_size(v); i++) { + const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i); + + const qpol_type_t *qt = apol_domain_trans_result_get_start_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + const char *name, *ep_name; + retval = qpol_type_get_name(q, qt, &name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_STRING_EQUAL(name, "tuna_t"); + + qt = apol_domain_trans_result_get_end_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + retval = qpol_type_get_name(q, qt, &name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(strcmp(name, "boat_t") == 0 || strcmp(name, "sand_t") == 0); + + qt = apol_domain_trans_result_get_entrypoint_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + retval = qpol_type_get_name(q, qt, &ep_name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + if (strcmp(name, "boat_t") == 0) { + CU_ASSERT_STRING_EQUAL(ep_name, "net_t"); + } else if (strcmp(name, "sand_t") == 0) { + CU_ASSERT(strcmp(ep_name, "reel_t") == 0 || strcmp(ep_name, "wave_t") == 0); + } + } + + apol_vector_destroy(&v); +} + +static void dta_forward_multi_end(void) +{ + apol_policy_reset_domain_trans_table(p); + apol_domain_trans_analysis_t *d = apol_domain_trans_analysis_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(d); + int retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_FORWARD); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_set_start_type(p, d, "shark_t"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + apol_vector_t *v = NULL; + retval = apol_domain_trans_analysis_do(p, d, &v); + apol_domain_trans_analysis_destroy(&d); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_PTR_NOT_NULL(v); + CU_ASSERT(apol_vector_get_size(v) == 2); + + qpol_policy_t *q = apol_policy_get_qpol(p); + size_t i; + for (i = 0; i < apol_vector_get_size(v); i++) { + const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i); + + const qpol_type_t *qt = apol_domain_trans_result_get_start_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + const char *name, *ep_name; + retval = qpol_type_get_name(q, qt, &name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_STRING_EQUAL(name, "shark_t"); + + qt = apol_domain_trans_result_get_end_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + retval = qpol_type_get_name(q, qt, &name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(strcmp(name, "surf_t") == 0 || strcmp(name, "sand_t") == 0); + + qt = apol_domain_trans_result_get_entrypoint_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + retval = qpol_type_get_name(q, qt, &ep_name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + CU_ASSERT_STRING_EQUAL(ep_name, "wave_t"); + } + + apol_vector_destroy(&v); +} + +static void dta_forward_access(void) +{ + apol_policy_reset_domain_trans_table(p); + apol_domain_trans_analysis_t *d = apol_domain_trans_analysis_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(d); + int retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_FORWARD); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_set_start_type(p, d, "tuna_t"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_append_access_type(p, d, "boat_t"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_append_access_type(p, d, "sand_t"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_append_access_type(p, d, "wave_t"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_append_class(p, d, "file"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_append_perm(p, d, "write"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + apol_vector_t *v = NULL; + retval = apol_domain_trans_analysis_do(p, d, &v); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) > 0); + + qpol_policy_t *q = apol_policy_get_qpol(p); + size_t i; + for (i = 0; i < apol_vector_get_size(v); i++) { + const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i); + + const qpol_type_t *qt = apol_domain_trans_result_get_start_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + const char *name; + retval = qpol_type_get_name(q, qt, &name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_STRING_EQUAL(name, "tuna_t"); + + qt = apol_domain_trans_result_get_end_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + retval = qpol_type_get_name(q, qt, &name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_STRING_EQUAL(name, "boat_t"); + + const apol_vector_t *rules_v = apol_domain_trans_result_get_access_rules(dtr); + CU_ASSERT_FATAL(rules_v != NULL && apol_vector_get_size(rules_v) > 0); + size_t j; + for (j = 0; j < apol_vector_get_size(rules_v); j++) { + const qpol_avrule_t *qa = (const qpol_avrule_t *)apol_vector_get_element(rules_v, j); + char *render = apol_avrule_render(p, qa); + CU_ASSERT_PTR_NOT_NULL_FATAL(render); + CU_ASSERT_STRING_EQUAL(render, "allow boat_t wave_t : file { write getattr execute } ;"); + free(render); + } + } + + apol_vector_destroy(&v); + + retval = apol_domain_trans_analysis_set_start_type(p, d, "boat_t"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_append_access_type(p, d, NULL); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_append_class(p, d, NULL); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_append_perm(p, d, NULL); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + apol_policy_reset_domain_trans_table(p); + retval = apol_domain_trans_analysis_do(p, d, &v); + apol_domain_trans_analysis_destroy(&d); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) > 0); + + for (i = 0; i < apol_vector_get_size(v); i++) { + const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i); + + const qpol_type_t *qt = apol_domain_trans_result_get_start_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + const char *name; + retval = qpol_type_get_name(q, qt, &name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_STRING_EQUAL(name, "boat_t"); + + qt = apol_domain_trans_result_get_end_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + retval = qpol_type_get_name(q, qt, &name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(strcmp(name, "sand_t") == 0 || strcmp(name, "dock_t") == 0); + } + apol_vector_destroy(&v); +} + +static void dta_reverse(void) +{ + apol_policy_reset_domain_trans_table(p); + apol_domain_trans_analysis_t *d = apol_domain_trans_analysis_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(d); + int retval; + retval = apol_domain_trans_analysis_set_start_type(p, d, "sand_t"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_REVERSE); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + apol_vector_t *v = NULL; + retval = apol_domain_trans_analysis_do(p, d, &v); + apol_domain_trans_analysis_destroy(&d); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) > 0); + + qpol_policy_t *q = apol_policy_get_qpol(p); + size_t i; + for (i = 0; i < apol_vector_get_size(v); i++) { + const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i); + + const qpol_type_t *qt = apol_domain_trans_result_get_end_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + const char *name; + retval = qpol_type_get_name(q, qt, &name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_STRING_EQUAL(name, "sand_t"); + + qt = apol_domain_trans_result_get_start_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + retval = qpol_type_get_name(q, qt, &name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(strcmp(name, "boat_t") == 0 || strcmp(name, "grouper_t") == 0 || strcmp(name, "shark_t") == 0 || + strcmp(name, "tuna_t") == 0); + } + + apol_vector_destroy(&v); +} + +static void dta_reverse_regexp(void) +{ + apol_policy_reset_domain_trans_table(p); + apol_domain_trans_analysis_t *d = apol_domain_trans_analysis_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(d); + int retval; + retval = apol_domain_trans_analysis_set_start_type(p, d, "sand_t"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_REVERSE); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_set_result_regex(p, d, "u"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + apol_vector_t *v = NULL; + retval = apol_domain_trans_analysis_do(p, d, &v); + apol_domain_trans_analysis_destroy(&d); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) > 0); + + qpol_policy_t *q = apol_policy_get_qpol(p); + size_t i; + bool found_tuna_wave = false, found_grouper_reel = false, found_grouper_wave = false; + for (i = 0; i < apol_vector_get_size(v); i++) { + const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i); + + const qpol_type_t *qt = apol_domain_trans_result_get_end_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + const char *name, *ep_name; + retval = qpol_type_get_name(q, qt, &name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_STRING_EQUAL(name, "sand_t"); + + qt = apol_domain_trans_result_get_start_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + retval = qpol_type_get_name(q, qt, &name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(strcmp(name, "tuna_t") == 0 || strcmp(name, "grouper_t") == 0); + + qt = apol_domain_trans_result_get_entrypoint_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + retval = qpol_type_get_name(q, qt, &ep_name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + if (strcmp(name, "tuna_t") == 0) { + if (strcmp(ep_name, "wave_t") == 0) { + found_tuna_wave = true; + } + } else if (strcmp(name, "grouper_t") == 0) { + if (strcmp(ep_name, "reel_t") == 0) { + found_grouper_reel = true; + } else if (strcmp(ep_name, "wave_t") == 0) { + found_grouper_wave = true; + } + } + } + CU_ASSERT(found_tuna_wave && found_grouper_reel && found_grouper_wave); + + apol_vector_destroy(&v); +} + +static void dta_reflexive(void) +{ + apol_policy_reset_domain_trans_table(p); + apol_domain_trans_analysis_t *d = apol_domain_trans_analysis_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(d); + int retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_FORWARD); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_set_start_type(p, d, "sand_t"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + apol_vector_t *v = NULL; + retval = apol_domain_trans_analysis_do(p, d, &v); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0); + apol_vector_destroy(&v); + + retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_REVERSE); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + retval = apol_domain_trans_analysis_do(p, d, &v); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) > 0); + size_t i; + qpol_policy_t *q = apol_policy_get_qpol(p); + for (i = 0; i < apol_vector_get_size(v); i++) { + const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i); + + const qpol_type_t *qt = apol_domain_trans_result_get_start_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + const char *name; + retval = qpol_type_get_name(q, qt, &name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_STRING_NOT_EQUAL(name, "sand_t"); + } + apol_vector_destroy(&v); + + apol_domain_trans_analysis_destroy(&d); +} + +struct dta_invalid_item +{ + const char *start_type; + const char *end_type; + const char *entrypoint_type; + const bool missing_proc_trans; + const bool missing_entrypoint; + const bool missing_exec; + const bool missing_setexec; + const bool missing_type_trans; + bool used; +}; + +static void dta_invalid(void) +{ + struct dta_invalid_item items[] = { + {"boat_t", "dock_t", "net_t", false, false, true, false, false, false}, + {"boat_t", "sand_t", "reel_t", false, false, true, false, false, false}, + {"crab_t", "dock_t", "net_t", false, false, false, true, false, false}, + {"crab_t", "dock_t", "rope_t", false, false, true, true, false, false}, + {"crab_t", "dock_t", "wave_t", false, true, true, false, false, false}, + {"gull_t", "dock_t", "net_t", false, false, false, true, true, false}, + {"gull_t", "dock_t", "rope_t", false, false, true, true, true, false}, + {"gull_t", "sand_t", "net_t", true, true, false, false, false, false}, + {"marlin_t", "boat_t", "line_t", false, false, true, false, false, false}, + {"marlin_t", "boat_t", "net_t", false, false, true, false, false, false}, + {"ray_t", "boat_t", "line_t", true, false, true, false, false, false}, + {"ray_t", "sand_t", "wave_t", true, false, false, false, false, false}, + {"shark_t", "sand_t", "reel_t", false, false, true, false, false, false}, + {"tuna_t", "boat_t", "line_t", false, false, true, false, false, false}, + {"tuna_t", "boat_t", "reel_t", false, true, false, false, false, false}, + {NULL, NULL, NULL, false, false, false, false, false, false} + }; + const char *start_types[] = { + "boat_t", "crab_t", "gull_t", "marlin_t", "ray_t", "shark_t", "tuna_t", NULL + }; + apol_domain_trans_analysis_t *d = apol_domain_trans_analysis_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(d); + int retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_FORWARD); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_domain_trans_analysis_set_valid(p, d, APOL_DOMAIN_TRANS_SEARCH_INVALID); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + qpol_policy_t *q = apol_policy_get_qpol(p); + apol_vector_t *v = NULL; + struct dta_invalid_item *item; + for (const char **start = start_types; *start != NULL; start++) { + apol_policy_reset_domain_trans_table(p); + retval = apol_domain_trans_analysis_set_start_type(p, d, *start); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + retval = apol_domain_trans_analysis_do(p, d, &v); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) > 0); + + for (size_t i = 0; i < apol_vector_get_size(v); i++) { + const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i); + + const char *result_start, *result_end, *result_entry; + + const qpol_type_t *qt = apol_domain_trans_result_get_start_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + retval = qpol_type_get_name(q, qt, &result_start); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_STRING_EQUAL(result_start, *start); + + qt = apol_domain_trans_result_get_end_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + retval = qpol_type_get_name(q, qt, &result_end); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + qt = apol_domain_trans_result_get_entrypoint_type(dtr); + CU_ASSERT_PTR_NOT_NULL(qt); + retval = qpol_type_get_name(q, qt, &result_entry); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + CU_ASSERT(apol_domain_trans_result_is_trans_valid(dtr) == 0); + + for (item = items + 0; item->start_type != NULL; item++) { + if (strcmp(result_start, item->start_type) == 0 && + strcmp(result_end, item->end_type) == 0 && + strcmp(result_entry, item->entrypoint_type) == 0 && !item->used) { + item->used = true; + + const apol_vector_t *cv; + if (item->missing_proc_trans) { + cv = apol_domain_trans_result_get_proc_trans_rules(dtr); + CU_ASSERT(cv != NULL && apol_vector_get_size(cv) == 0); + } + if (item->missing_entrypoint) { + cv = apol_domain_trans_result_get_entrypoint_rules(dtr); + CU_ASSERT(cv != NULL && apol_vector_get_size(cv) == 0); + } + if (item->missing_exec) { + cv = apol_domain_trans_result_get_exec_rules(dtr); + CU_ASSERT(cv != NULL && apol_vector_get_size(cv) == 0); + } + if (item->missing_setexec) { + cv = apol_domain_trans_result_get_setexec_rules(dtr); + CU_ASSERT(cv != NULL && apol_vector_get_size(cv) == 0); + } + if (item->missing_type_trans) { + cv = apol_domain_trans_result_get_type_trans_rules(dtr); + CU_ASSERT(cv != NULL && apol_vector_get_size(cv) == 0); + } + break; + } + } + if (item->start_type == NULL) { + CU_FAIL(); + } + } + apol_vector_destroy(&v); + } + + for (item = items + 0; item->start_type != NULL; item++) { + CU_ASSERT(item->used); + } + apol_domain_trans_analysis_destroy(&d); +} + +CU_TestInfo dta_tests[] = { + {"dta forward", dta_forward} + , + {"dta forward + access", dta_forward_access} + , + {"dta forward with multiple endpoints for same entrypoint", dta_forward_multi_end} + , + {"dta reverse", dta_reverse} + , + {"dta reverse + regexp", dta_reverse_regexp} + , + {"dta reflexive", dta_reflexive} + , + {"dta invalid transitions", dta_invalid} + , + CU_TEST_INFO_NULL +}; + +int dta_init() +{ + apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, POLICY, NULL); + if (ppath == NULL) { + return 1; + } + + if ((p = apol_policy_create_from_policy_path(ppath, QPOL_POLICY_OPTION_NO_NEVERALLOWS, NULL, NULL)) == NULL) { + apol_policy_path_destroy(&ppath); + return 1; + } + apol_policy_path_destroy(&ppath); + + int retval = apol_policy_build_domain_trans_table(p); + if (retval != 0) { + return 1; + } + return 0; +} + +int dta_cleanup() +{ + apol_policy_destroy(&p); + return 0; +} diff --git a/libapol/tests/dta-tests.h b/libapol/tests/dta-tests.h new file mode 100644 index 0000000..820b8d2 --- /dev/null +++ b/libapol/tests/dta-tests.h @@ -0,0 +1,35 @@ +/** + * @file + * + * Declarations for libapol domain transition analysis tests. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DTA_TESTS_H +#define DTA_TESTS_H + +#include <CUnit/CUnit.h> + +extern CU_TestInfo dta_tests[]; +extern int dta_init(); +extern int dta_cleanup(); + +#endif diff --git a/libapol/tests/infoflow-tests.c b/libapol/tests/infoflow-tests.c new file mode 100644 index 0000000..6a74ba6 --- /dev/null +++ b/libapol/tests/infoflow-tests.c @@ -0,0 +1,127 @@ +/** + * @file + * + * Test the information flow analysis code. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <CUnit/CUnit.h> +#include <apol/infoflow-analysis.h> +#include <apol/perm-map.h> +#include <apol/policy.h> +#include <apol/policy-path.h> +#include <stdbool.h> +#include <string.h> + +#define BIG_POLICY TEST_POLICIES "/snapshots/fc4_targeted.policy.conf" +#define PERMMAP TOP_SRCDIR "/apol/perm_maps/apol_perm_mapping_ver19" + +static apol_policy_t *p = NULL; + +static void infoflow_direct_overview(void) +{ + apol_infoflow_analysis_t *ia = apol_infoflow_analysis_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(ia); + int retval; + retval = apol_infoflow_analysis_set_mode(p, ia, APOL_INFOFLOW_MODE_DIRECT); + CU_ASSERT(retval == 0); + retval = apol_infoflow_analysis_set_dir(p, ia, APOL_INFOFLOW_IN); + CU_ASSERT(retval == 0); + retval = apol_infoflow_analysis_set_type(p, ia, "agp_device_t"); + CU_ASSERT(retval == 0); + + apol_vector_t *v = NULL; + apol_infoflow_graph_t *g = NULL; + // no permmap loaded, so analysis run will abort with error + retval = apol_infoflow_analysis_do(p, ia, &v, &g); + CU_ASSERT(retval < 0); + + retval = apol_policy_open_permmap(p, PERMMAP); + CU_ASSERT(retval == 0); + + retval = apol_infoflow_analysis_do(p, ia, &v, &g); + CU_ASSERT(retval == 0); + CU_ASSERT_PTR_NOT_NULL(v); + CU_ASSERT(apol_vector_get_size(v) > 0); + CU_ASSERT_PTR_NOT_NULL(g); + + apol_infoflow_analysis_destroy(&ia); + apol_vector_destroy(&v); + apol_infoflow_graph_destroy(&g); +} + +static void infoflow_trans_overview(void) +{ + apol_infoflow_analysis_t *ia = apol_infoflow_analysis_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(ia); + int retval; + retval = apol_infoflow_analysis_set_mode(p, ia, APOL_INFOFLOW_MODE_DIRECT); + CU_ASSERT(retval == 0); + retval = apol_infoflow_analysis_set_dir(p, ia, APOL_INFOFLOW_IN); + CU_ASSERT(retval == 0); + retval = apol_infoflow_analysis_set_type(p, ia, "local_login_t"); + CU_ASSERT(retval == 0); + + apol_vector_t *v = NULL; + apol_infoflow_graph_t *g = NULL; + // permmap was loaded by infoflow_direct_overview() + retval = apol_infoflow_analysis_do(p, ia, &v, &g); + CU_ASSERT(retval == 0); + CU_ASSERT_PTR_NOT_NULL(v); + CU_ASSERT(apol_vector_get_size(v) > 0); + CU_ASSERT_PTR_NOT_NULL(g); + + apol_infoflow_analysis_destroy(&ia); + apol_vector_destroy(&v); + apol_infoflow_graph_destroy(&g); +} + +CU_TestInfo infoflow_tests[] = { + {"infoflow direct overview", infoflow_direct_overview} + , + {"infoflow trans overview", infoflow_trans_overview} + , + CU_TEST_INFO_NULL +}; + +int infoflow_init() +{ + apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, BIG_POLICY, NULL); + if (ppath == NULL) { + return 1; + } + + if ((p = apol_policy_create_from_policy_path(ppath, QPOL_POLICY_OPTION_NO_NEVERALLOWS, NULL, NULL)) == NULL) { + apol_policy_path_destroy(&ppath); + return 1; + } + apol_policy_path_destroy(&ppath); + + return 0; +} + +int infoflow_cleanup() +{ + apol_policy_destroy(&p); + return 0; +} diff --git a/libapol/tests/infoflow-tests.h b/libapol/tests/infoflow-tests.h new file mode 100644 index 0000000..840f3b1 --- /dev/null +++ b/libapol/tests/infoflow-tests.h @@ -0,0 +1,36 @@ +/** + * @file + * + * Declarations for libapol infomation flow analysis tests, both + * direct and transitive. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INFOFLOW_TESTS_H +#define INFOFLOW_TESTS_H + +#include <CUnit/CUnit.h> + +extern CU_TestInfo infoflow_tests[]; +extern int infoflow_init(); +extern int infoflow_cleanup(); + +#endif diff --git a/libapol/tests/libapol-tests.c b/libapol/tests/libapol-tests.c new file mode 100644 index 0000000..9b83235 --- /dev/null +++ b/libapol/tests/libapol-tests.c @@ -0,0 +1,64 @@ +/** + * @file + * + * CUnit testing framework for libapol. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <CUnit/CUnit.h> +#include <CUnit/Basic.h> + +#include "avrule-tests.h" +#include "dta-tests.h" +#include "infoflow-tests.h" +#include "policy-21-tests.h" +#include "role-tests.h" +#include "terule-tests.h" +#include "constrain-tests.h" +#include "user-tests.h" + +int main(void) +{ + if (CU_initialize_registry() != CUE_SUCCESS) { + return CU_get_error(); + } + + CU_SuiteInfo suites[] = { + {"Policy Version 21", policy_21_init, policy_21_cleanup, policy_21_tests}, + {"AV Rule Query", avrule_init, avrule_cleanup, avrule_tests}, + {"Domain Transition Analysis", dta_init, dta_cleanup, dta_tests}, + {"Infoflow Analysis", infoflow_init, infoflow_cleanup, infoflow_tests}, + {"Role Query", role_init, role_cleanup, role_tests}, + {"TE Rule Query", terule_init, terule_cleanup, terule_tests}, + {"User Query", user_init, user_cleanup, user_tests}, + {"Constrain query", constrain_init, constrain_cleanup, constrain_tests}, + CU_SUITE_INFO_NULL + }; + + CU_register_suites(suites); + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + unsigned int num_failures = CU_get_number_of_failure_records(); + CU_cleanup_registry(); + return (int)num_failures; +} diff --git a/libapol/tests/policy-21-tests.c b/libapol/tests/policy-21-tests.c new file mode 100644 index 0000000..ea07da1 --- /dev/null +++ b/libapol/tests/policy-21-tests.c @@ -0,0 +1,181 @@ +/** + * @file + * + * Test features of policy version 21, that were introduced in + * SETools 3.2. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <CUnit/CUnit.h> +#include <apol/policy.h> +#include <apol/policy-path.h> +#include <apol/range_trans-query.h> + +#define POLICY TEST_POLICIES "/setools-3.2/apol/rangetrans_testing_policy.conf" + +static apol_policy_t *p = NULL; + +static void policy_21_range_trans_all(void) +{ + apol_range_trans_query_t *rt = apol_range_trans_query_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(rt); + + apol_vector_t *v = NULL; + int retval = apol_range_trans_get_by_query(p, rt, &v); + apol_range_trans_query_destroy(&rt); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 17); + apol_vector_destroy(&v); +} + +static void policy_21_range_trans_process(void) +{ + apol_range_trans_query_t *rt = apol_range_trans_query_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(rt); + int retval; + retval = apol_range_trans_query_append_class(p, rt, "process"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + apol_vector_t *v = NULL; + retval = apol_range_trans_get_by_query(p, rt, &v); + apol_range_trans_query_destroy(&rt); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 10); + size_t i; + qpol_policy_t *q = apol_policy_get_qpol(p); + for (i = 0; i < apol_vector_get_size(v); i++) { + const qpol_range_trans_t *qrt = (const qpol_range_trans_t *)apol_vector_get_element(v, i); + const qpol_class_t *c; + retval = qpol_range_trans_get_target_class(q, qrt, &c); + CU_ASSERT_EQUAL_FATAL(retval, 0); + const char *class_name; + retval = qpol_class_get_name(q, c, &class_name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_STRING_EQUAL(class_name, "process"); + } + apol_vector_destroy(&v); +} + +static void policy_21_range_trans_lnk_file(void) +{ + apol_range_trans_query_t *rt = apol_range_trans_query_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(rt); + int retval; + retval = apol_range_trans_query_append_class(p, rt, "lnk_file"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + apol_vector_t *v = NULL; + retval = apol_range_trans_get_by_query(p, rt, &v); + apol_range_trans_query_destroy(&rt); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 2); + size_t i; + qpol_policy_t *q = apol_policy_get_qpol(p); + for (i = 0; i < apol_vector_get_size(v); i++) { + const qpol_range_trans_t *qrt = (const qpol_range_trans_t *)apol_vector_get_element(v, i); + const qpol_class_t *c; + retval = qpol_range_trans_get_target_class(q, qrt, &c); + CU_ASSERT_EQUAL_FATAL(retval, 0); + const char *class_name; + retval = qpol_class_get_name(q, c, &class_name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_STRING_EQUAL(class_name, "lnk_file"); + } + apol_vector_destroy(&v); +} + +static void policy_21_range_trans_either(void) +{ + apol_range_trans_query_t *rt = apol_range_trans_query_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(rt); + int retval; + retval = apol_range_trans_query_append_class(p, rt, "process"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + retval = apol_range_trans_query_append_class(p, rt, "lnk_file"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + apol_vector_t *v = NULL; + retval = apol_range_trans_get_by_query(p, rt, &v); + apol_range_trans_query_destroy(&rt); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 12); + size_t i; + qpol_policy_t *q = apol_policy_get_qpol(p); + for (i = 0; i < apol_vector_get_size(v); i++) { + const qpol_range_trans_t *qrt = (const qpol_range_trans_t *)apol_vector_get_element(v, i); + const qpol_class_t *c; + retval = qpol_range_trans_get_target_class(q, qrt, &c); + CU_ASSERT_EQUAL_FATAL(retval, 0); + const char *class_name; + retval = qpol_class_get_name(q, c, &class_name); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(strcmp(class_name, "process") == 0 || strcmp(class_name, "lnk_file") == 0); + } + apol_vector_destroy(&v); +} + +static void policy_21_range_trans_socket(void) +{ + apol_range_trans_query_t *rt = apol_range_trans_query_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(rt); + int retval; + retval = apol_range_trans_query_append_class(p, rt, "socket"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + apol_vector_t *v = NULL; + retval = apol_range_trans_get_by_query(p, rt, &v); + apol_range_trans_query_destroy(&rt); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0); + apol_vector_destroy(&v); +} + +CU_TestInfo policy_21_tests[] = { + {"range_trans all", policy_21_range_trans_all}, + {"range_trans process", policy_21_range_trans_process}, + {"range_trans lnk_file", policy_21_range_trans_lnk_file}, + {"range_trans process or lnk_file", policy_21_range_trans_either}, + {"range_trans socket", policy_21_range_trans_socket}, + CU_TEST_INFO_NULL +}; + +int policy_21_init() +{ + apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, POLICY, NULL); + if (ppath == NULL) { + return 1; + } + + if ((p = apol_policy_create_from_policy_path(ppath, QPOL_POLICY_OPTION_NO_RULES, NULL, NULL)) == NULL) { + apol_policy_path_destroy(&ppath); + return 1; + } + apol_policy_path_destroy(&ppath); + return 0; +} + +int policy_21_cleanup() +{ + apol_policy_destroy(&p); + return 0; +} diff --git a/libapol/tests/policy-21-tests.h b/libapol/tests/policy-21-tests.h new file mode 100644 index 0000000..dd427ba --- /dev/null +++ b/libapol/tests/policy-21-tests.h @@ -0,0 +1,35 @@ +/** + * @file + * + * Declarations for libapol version 21 policy support. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef POLICY_21_TESTS_H +#define POLICY_21_TESTS_H + +#include <CUnit/CUnit.h> + +extern CU_TestInfo policy_21_tests[]; +extern int policy_21_init(); +extern int policy_21_cleanup(); + +#endif diff --git a/libapol/tests/role-tests.c b/libapol/tests/role-tests.c new file mode 100644 index 0000000..3aee323 --- /dev/null +++ b/libapol/tests/role-tests.c @@ -0,0 +1,154 @@ +/** + * @file + * + * Test the role queries. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <CUnit/CUnit.h> +#include <apol/role-query.h> +#include <apol/policy.h> +#include <apol/policy-path.h> +#include <stdbool.h> + +#define SOURCE_POLICY TEST_POLICIES "/setools/apol/role_dom.conf" + +static apol_policy_t *sp = NULL; +static qpol_policy_t *qp = NULL; + +static void role_basic(void) +{ + apol_role_query_t *q = apol_role_query_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(q); + + apol_vector_t *v = NULL; + CU_ASSERT(apol_role_get_by_query(sp, q, &v) == 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 26); + apol_vector_destroy(&v); + + apol_role_query_set_role(sp, q, "sh"); + CU_ASSERT(apol_role_get_by_query(sp, q, &v) == 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0); + apol_vector_destroy(&v); + + apol_role_query_set_role(sp, q, NULL); + apol_role_query_set_type(sp, q, "silly_t"); + CU_ASSERT(apol_role_get_by_query(sp, q, &v) == 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 2); + bool found_silly = false, found_object = false; + for (size_t i = 0; i < apol_vector_get_size(v); i++) { + qpol_role_t *r = (qpol_role_t *) apol_vector_get_element(v, i); + const char *name; + qpol_role_get_name(qp, r, &name); + if (strcmp(name, "silly_r") == 0) { + found_silly = true; + } else if (strcmp(name, "object_r") == 0) { + found_object = true; + } else { + CU_ASSERT(0); + } + } + CU_ASSERT(found_silly && found_object); + apol_vector_destroy(&v); + + apol_role_query_set_type(sp, q, "not_in_the_policy_t"); + CU_ASSERT(apol_role_get_by_query(sp, q, &v) == 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0); + apol_vector_destroy(&v); + + apol_role_query_destroy(&q); +} + +static void role_regex(void) +{ + apol_role_query_t *q = apol_role_query_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(q); + apol_role_query_set_regex(sp, q, 1); + + apol_role_query_set_role(sp, q, "*"); + apol_vector_t *v = NULL; + CU_ASSERT(apol_role_get_by_query(sp, q, &v) < 0 && v == NULL); + + apol_role_query_set_role(sp, q, "^sh"); + CU_ASSERT(apol_role_get_by_query(sp, q, &v) == 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 2); + bool found_shirt = false, found_shoe = false; + for (size_t i = 0; i < apol_vector_get_size(v); i++) { + qpol_role_t *r = (qpol_role_t *) apol_vector_get_element(v, i); + const char *name; + qpol_role_get_name(qp, r, &name); + if (strcmp(name, "shirt_r") == 0) { + found_shirt = true; + } else if (strcmp(name, "shoe_r") == 0) { + found_shoe = true; + } else { + CU_ASSERT(0); + } + } + CU_ASSERT(found_shirt && found_shoe); + apol_vector_destroy(&v); + + apol_role_query_set_role(sp, q, NULL); + apol_role_query_set_type(sp, q, "file"); + CU_ASSERT(apol_role_get_by_query(sp, q, &v) == 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 1); + qpol_role_t *r = (qpol_role_t *) apol_vector_get_element(v, 0); + const char *name; + qpol_role_get_name(qp, r, &name); + CU_ASSERT_STRING_EQUAL(name, "object_r"); + apol_vector_destroy(&v); + + apol_role_query_destroy(&q); +} + +CU_TestInfo role_tests[] = { + {"basic query", role_basic} + , + {"regex query", role_regex} + , + CU_TEST_INFO_NULL +}; + +int role_init() +{ + apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, SOURCE_POLICY, NULL); + if (ppath == NULL) { + return 1; + } + + if ((sp = apol_policy_create_from_policy_path(ppath, 0, NULL, NULL)) == NULL) { + apol_policy_path_destroy(&ppath); + return 1; + } + apol_policy_path_destroy(&ppath); + + qp = apol_policy_get_qpol(sp); + + return 0; +} + +int role_cleanup() +{ + apol_policy_destroy(&sp); + return 0; +} diff --git a/libapol/tests/role-tests.h b/libapol/tests/role-tests.h new file mode 100644 index 0000000..0663f9e --- /dev/null +++ b/libapol/tests/role-tests.h @@ -0,0 +1,35 @@ +/** + * @file + * + * Declarations for libapol role query tests. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ROLE_TESTS_H +#define ROLE_TESTS_H + +#include <CUnit/CUnit.h> + +extern CU_TestInfo role_tests[]; +extern int role_init(); +extern int role_cleanup(); + +#endif diff --git a/libapol/tests/terule-tests.c b/libapol/tests/terule-tests.c new file mode 100644 index 0000000..f635e02 --- /dev/null +++ b/libapol/tests/terule-tests.c @@ -0,0 +1,130 @@ +/** + * @file + * + * Test the TE rule queries, both semantic and syntactic searches. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <CUnit/CUnit.h> +#include <apol/policy.h> +#include <apol/policy-path.h> +#include <apol/terule-query.h> +#include <qpol/policy_extend.h> +#include <stdbool.h> + +#define BIN_POLICY TEST_POLICIES "/setools-3.3/rules/rules-mls.21" +#define SOURCE_POLICY TEST_POLICIES "/setools-3.3/rules/rules-mls.conf" + +static apol_policy_t *bp = NULL; +static apol_policy_t *sp = NULL; + +static void terule_basic_syn(void) +{ + apol_terule_query_t *tq = apol_terule_query_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(tq); + + int retval; + retval = apol_terule_query_set_rules(sp, tq, QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE | QPOL_RULE_TYPE_MEMBER); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + apol_vector_t *v = NULL; + retval = apol_syn_terule_get_by_query(sp, tq, &v); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT_PTR_NOT_NULL(v); + + size_t num_trans = 0, num_changes = 0, num_members = 0; + + qpol_policy_t *q = apol_policy_get_qpol(sp); + size_t i; + for (i = 0; i < apol_vector_get_size(v); i++) { + const qpol_syn_terule_t *syn = (const qpol_syn_terule_t *)apol_vector_get_element(v, i); + uint32_t rule_type; + retval = qpol_syn_terule_get_rule_type(q, syn, &rule_type); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(rule_type == QPOL_RULE_TYPE_TRANS || rule_type == QPOL_RULE_TYPE_CHANGE || + rule_type == QPOL_RULE_TYPE_MEMBER); + + if (rule_type == QPOL_RULE_TYPE_TRANS) { + num_trans++; + } else if (rule_type == QPOL_RULE_TYPE_CHANGE) { + num_changes++; + } else if (rule_type == QPOL_RULE_TYPE_MEMBER) { + num_members++; + } + } + CU_ASSERT(num_trans == 6 && num_changes == 3 && num_members == 4); + apol_vector_destroy(&v); + + retval = apol_terule_query_append_class(sp, tq, "cursor"); + CU_ASSERT_EQUAL_FATAL(retval, 0); + + retval = apol_syn_terule_get_by_query(sp, tq, &v); + CU_ASSERT_EQUAL_FATAL(retval, 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0); + apol_vector_destroy(&v); + apol_terule_query_destroy(&tq); +} + +CU_TestInfo terule_tests[] = { + {"basic syntactic search", terule_basic_syn} + , + CU_TEST_INFO_NULL +}; + +int terule_init() +{ + apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, BIN_POLICY, NULL); + if (ppath == NULL) { + return 1; + } + + if ((bp = apol_policy_create_from_policy_path(ppath, 0, NULL, NULL)) == NULL) { + apol_policy_path_destroy(&ppath); + return 1; + } + apol_policy_path_destroy(&ppath); + + ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, SOURCE_POLICY, NULL); + if (ppath == NULL) { + return 1; + } + + if ((sp = apol_policy_create_from_policy_path(ppath, 0, NULL, NULL)) == NULL) { + apol_policy_path_destroy(&ppath); + return 1; + } + apol_policy_path_destroy(&ppath); + + if (qpol_policy_build_syn_rule_table(apol_policy_get_qpol(sp)) != 0) { + return 1; + } + + return 0; +} + +int terule_cleanup() +{ + apol_policy_destroy(&bp); + apol_policy_destroy(&sp); + return 0; +} diff --git a/libapol/tests/terule-tests.h b/libapol/tests/terule-tests.h new file mode 100644 index 0000000..fff497a --- /dev/null +++ b/libapol/tests/terule-tests.h @@ -0,0 +1,35 @@ +/** + * @file + * + * Declarations for libapol terule query tests. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef TERULE_TESTS_H +#define TERULE_TESTS_H + +#include <CUnit/CUnit.h> + +extern CU_TestInfo terule_tests[]; +extern int terule_init(); +extern int terule_cleanup(); + +#endif diff --git a/libapol/tests/user-tests.c b/libapol/tests/user-tests.c new file mode 100644 index 0000000..2d912c0 --- /dev/null +++ b/libapol/tests/user-tests.c @@ -0,0 +1,159 @@ +/** + * @file + * + * Test the user queries. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <CUnit/CUnit.h> +#include <apol/user-query.h> +#include <apol/policy.h> +#include <apol/policy-path.h> +#include <stdbool.h> + +#define SOURCE_POLICY TEST_POLICIES "/setools/apol/user_mls_testing_policy.conf" + +static apol_policy_t *sp = NULL; +static qpol_policy_t *qp = NULL; + +static void user_basic(void) +{ + apol_user_query_t *q = apol_user_query_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(q); + + apol_vector_t *v = NULL; + CU_ASSERT(apol_user_get_by_query(sp, q, &v) == 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 10); + apol_vector_destroy(&v); + + apol_user_query_set_role(sp, q, "object_r"); + CU_ASSERT(apol_user_get_by_query(sp, q, &v) == 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 10); + apol_vector_destroy(&v); + + apol_user_query_set_user(sp, q, "sys"); + CU_ASSERT(apol_user_get_by_query(sp, q, &v) == 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0); + apol_vector_destroy(&v); + + apol_user_query_set_user(sp, q, NULL); + apol_user_query_set_role(sp, q, "staff_r"); + CU_ASSERT(apol_user_get_by_query(sp, q, &v) == 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 3); + bool found_staff = false, found_rick = false, found_simple = false; + for (size_t i = 0; i < apol_vector_get_size(v); i++) { + qpol_user_t *u = (qpol_user_t *) apol_vector_get_element(v, i); + const char *name; + qpol_user_get_name(qp, u, &name); + if (strcmp(name, "staff_u") == 0) { + found_staff = true; + } else if (strcmp(name, "rick_u") == 0) { + found_rick = true; + } else if (strcmp(name, "simple_u") == 0) { + found_simple = true; + } else { + CU_ASSERT(0); + } + } + CU_ASSERT(found_staff && found_rick && found_simple); + apol_vector_destroy(&v); + + apol_user_query_set_role(sp, q, "not_in_the_policy_r"); + CU_ASSERT(apol_user_get_by_query(sp, q, &v) == 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0); + apol_vector_destroy(&v); + + apol_user_query_destroy(&q); +} + +static void user_regex(void) +{ + apol_user_query_t *q = apol_user_query_create(); + CU_ASSERT_PTR_NOT_NULL_FATAL(q); + apol_user_query_set_regex(sp, q, 1); + + apol_user_query_set_user(sp, q, "*"); + apol_vector_t *v = NULL; + CU_ASSERT(apol_user_get_by_query(sp, q, &v) < 0 && v == NULL); + + apol_user_query_set_user(sp, q, "st"); + CU_ASSERT(apol_user_get_by_query(sp, q, &v) == 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 3); + bool found_staff = false, found_system = false, found_guest = false; + for (size_t i = 0; i < apol_vector_get_size(v); i++) { + qpol_user_t *u = (qpol_user_t *) apol_vector_get_element(v, i); + const char *name; + qpol_user_get_name(qp, u, &name); + if (strcmp(name, "staff_u") == 0) { + found_staff = true; + } else if (strcmp(name, "system_u") == 0) { + found_system = true; + } else if (strcmp(name, "guest_u") == 0) { + found_guest = true; + } else { + CU_ASSERT(0); + } + } + CU_ASSERT(found_staff && found_system && found_guest); + apol_vector_destroy(&v); + + apol_user_query_set_user(sp, q, NULL); + apol_user_query_set_role(sp, q, "user_r"); + CU_ASSERT(apol_user_get_by_query(sp, q, &v) == 0); + CU_ASSERT(v != NULL && apol_vector_get_size(v) == 3); + apol_vector_destroy(&v); + + apol_user_query_destroy(&q); +} + +CU_TestInfo user_tests[] = { + {"basic query", user_basic} + , + {"regex query", user_regex} + , + CU_TEST_INFO_NULL +}; + +int user_init() +{ + apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, SOURCE_POLICY, NULL); + if (ppath == NULL) { + return 1; + } + + if ((sp = apol_policy_create_from_policy_path(ppath, 0, NULL, NULL)) == NULL) { + apol_policy_path_destroy(&ppath); + return 1; + } + apol_policy_path_destroy(&ppath); + + qp = apol_policy_get_qpol(sp); + + return 0; +} + +int user_cleanup() +{ + apol_policy_destroy(&sp); + return 0; +} diff --git a/libapol/tests/user-tests.h b/libapol/tests/user-tests.h new file mode 100644 index 0000000..d725db4 --- /dev/null +++ b/libapol/tests/user-tests.h @@ -0,0 +1,35 @@ +/** + * @file + * + * Declarations for libapol user query tests. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef USER_TESTS_H +#define USER_TESTS_H + +#include <CUnit/CUnit.h> + +extern CU_TestInfo user_tests[]; +extern int user_init(); +extern int user_cleanup(); + +#endif |