summaryrefslogtreecommitdiffstats
path: root/libapol
diff options
context:
space:
mode:
Diffstat (limited to 'libapol')
-rw-r--r--libapol/Makefile.am8
-rw-r--r--libapol/include/Makefile.am1
-rw-r--r--libapol/include/apol/Makefile.am35
-rw-r--r--libapol/include/apol/avrule-query.h373
-rw-r--r--libapol/include/apol/bool-query.h105
-rw-r--r--libapol/include/apol/bst.h178
-rw-r--r--libapol/include/apol/class-perm-query.h255
-rw-r--r--libapol/include/apol/condrule-query.h119
-rw-r--r--libapol/include/apol/constraint-query.h188
-rw-r--r--libapol/include/apol/context-query.h261
-rw-r--r--libapol/include/apol/domain-trans-analysis.h427
-rw-r--r--libapol/include/apol/fscon-query.h249
-rw-r--r--libapol/include/apol/infoflow-analysis.h387
-rw-r--r--libapol/include/apol/isid-query.h111
-rw-r--r--libapol/include/apol/mls-query.h228
-rw-r--r--libapol/include/apol/mls_level.h261
-rw-r--r--libapol/include/apol/mls_range.h270
-rw-r--r--libapol/include/apol/netcon-query.h364
-rw-r--r--libapol/include/apol/perm-map.h137
-rw-r--r--libapol/include/apol/permissive-query.h104
-rw-r--r--libapol/include/apol/polcap-query.h103
-rw-r--r--libapol/include/apol/policy-path.h194
-rw-r--r--libapol/include/apol/policy-query.h86
-rw-r--r--libapol/include/apol/policy.h166
-rw-r--r--libapol/include/apol/range_trans-query.h198
-rw-r--r--libapol/include/apol/rbacrule-query.h279
-rw-r--r--libapol/include/apol/relabel-analysis.h258
-rw-r--r--libapol/include/apol/render.h82
-rw-r--r--libapol/include/apol/role-query.h127
-rw-r--r--libapol/include/apol/terule-query.h321
-rw-r--r--libapol/include/apol/type-query.h172
-rw-r--r--libapol/include/apol/types-relation-analysis.h380
-rw-r--r--libapol/include/apol/user-query.h150
-rw-r--r--libapol/include/apol/util.h336
-rw-r--r--libapol/include/apol/vector.h335
-rw-r--r--libapol/src/Makefile.am75
-rw-r--r--libapol/src/avrule-query.c1209
-rw-r--r--libapol/src/bool-query.c111
-rw-r--r--libapol/src/bst.c352
-rw-r--r--libapol/src/class-perm-query.c327
-rw-r--r--libapol/src/condrule-query.c182
-rw-r--r--libapol/src/constraint-query.c217
-rw-r--r--libapol/src/context-query.c477
-rw-r--r--libapol/src/domain-trans-analysis-internal.h36
-rw-r--r--libapol/src/domain-trans-analysis.c2076
-rw-r--r--libapol/src/fscon-query.c437
-rw-r--r--libapol/src/infoflow-analysis-internal.h49
-rw-r--r--libapol/src/infoflow-analysis.c2245
-rw-r--r--libapol/src/isid-query.c123
-rw-r--r--libapol/src/libapol.map86
-rw-r--r--libapol/src/mls-query.c248
-rw-r--r--libapol/src/mls_level.c771
-rw-r--r--libapol/src/mls_range.c641
-rw-r--r--libapol/src/netcon-query.c592
-rw-r--r--libapol/src/perm-map.c697
-rw-r--r--libapol/src/permissive-query.c107
-rw-r--r--libapol/src/polcap-query.c107
-rw-r--r--libapol/src/policy-path.c409
-rw-r--r--libapol/src/policy-query-internal.h511
-rw-r--r--libapol/src/policy-query.c903
-rw-r--r--libapol/src/policy.c217
-rw-r--r--libapol/src/queue.c125
-rw-r--r--libapol/src/queue.h86
-rw-r--r--libapol/src/range_trans-query.c318
-rw-r--r--libapol/src/rbacrule-query.c417
-rw-r--r--libapol/src/relabel-analysis.c813
-rw-r--r--libapol/src/render.c158
-rw-r--r--libapol/src/role-query.c167
-rw-r--r--libapol/src/terule-query.c1049
-rw-r--r--libapol/src/type-query.c202
-rw-r--r--libapol/src/types-relation-analysis.c1143
-rw-r--r--libapol/src/user-query.c198
-rw-r--r--libapol/src/util.c659
-rw-r--r--libapol/src/vector-internal.h36
-rw-r--r--libapol/src/vector.c457
-rw-r--r--libapol/swig/Makefile.am15
-rw-r--r--libapol/swig/apol.i3220
-rw-r--r--libapol/swig/java/MANIFEST.MF.in12
-rw-r--r--libapol/swig/java/Makefile.am118
-rw-r--r--libapol/swig/python/Makefile.am36
-rw-r--r--libapol/swig/tcl/Makefile.am36
-rw-r--r--libapol/tests/Makefile.am23
-rw-r--r--libapol/tests/avrule-tests.c161
-rw-r--r--libapol/tests/avrule-tests.h35
-rw-r--r--libapol/tests/constrain-tests.c523
-rw-r--r--libapol/tests/constrain-tests.h33
-rw-r--r--libapol/tests/dta-tests.c529
-rw-r--r--libapol/tests/dta-tests.h35
-rw-r--r--libapol/tests/infoflow-tests.c127
-rw-r--r--libapol/tests/infoflow-tests.h36
-rw-r--r--libapol/tests/libapol-tests.c64
-rw-r--r--libapol/tests/policy-21-tests.c181
-rw-r--r--libapol/tests/policy-21-tests.h35
-rw-r--r--libapol/tests/role-tests.c154
-rw-r--r--libapol/tests/role-tests.h35
-rw-r--r--libapol/tests/terule-tests.c130
-rw-r--r--libapol/tests/terule-tests.h35
-rw-r--r--libapol/tests/user-tests.c159
-rw-r--r--libapol/tests/user-tests.h35
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(&ltime) == (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(&ltime)) < 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, &regex);
+ 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, &regex);
+ 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, &regex);
+ 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, &regex);
+ 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, &regex);
+ 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