summaryrefslogtreecommitdiffstats
path: root/libpoldiff
diff options
context:
space:
mode:
Diffstat (limited to 'libpoldiff')
-rw-r--r--libpoldiff/Makefile.am8
-rw-r--r--libpoldiff/include/Makefile.am1
-rw-r--r--libpoldiff/include/poldiff/Makefile.am20
-rw-r--r--libpoldiff/include/poldiff/attrib_diff.h130
-rw-r--r--libpoldiff/include/poldiff/avrule_diff.h361
-rw-r--r--libpoldiff/include/poldiff/bool_diff.h146
-rw-r--r--libpoldiff/include/poldiff/cat_diff.h103
-rw-r--r--libpoldiff/include/poldiff/class_diff.h222
-rw-r--r--libpoldiff/include/poldiff/component_record.h159
-rw-r--r--libpoldiff/include/poldiff/level_diff.h159
-rw-r--r--libpoldiff/include/poldiff/poldiff.h218
-rw-r--r--libpoldiff/include/poldiff/range_diff.h129
-rw-r--r--libpoldiff/include/poldiff/range_trans_diff.h140
-rw-r--r--libpoldiff/include/poldiff/rbac_diff.h251
-rw-r--r--libpoldiff/include/poldiff/role_diff.h127
-rw-r--r--libpoldiff/include/poldiff/terule_diff.h262
-rw-r--r--libpoldiff/include/poldiff/type_diff.h132
-rw-r--r--libpoldiff/include/poldiff/type_map.h153
-rw-r--r--libpoldiff/include/poldiff/user_diff.h191
-rw-r--r--libpoldiff/include/poldiff/util.h45
-rw-r--r--libpoldiff/src/Makefile.am57
-rw-r--r--libpoldiff/src/attrib_diff.c544
-rw-r--r--libpoldiff/src/attrib_internal.h121
-rw-r--r--libpoldiff/src/avrule_diff.c1636
-rw-r--r--libpoldiff/src/avrule_internal.h296
-rw-r--r--libpoldiff/src/bool_diff.c333
-rw-r--r--libpoldiff/src/bool_internal.h122
-rw-r--r--libpoldiff/src/cat_diff.c289
-rw-r--r--libpoldiff/src/cat_internal.h120
-rw-r--r--libpoldiff/src/class_diff.c990
-rw-r--r--libpoldiff/src/class_internal.h212
-rw-r--r--libpoldiff/src/level_diff.c769
-rw-r--r--libpoldiff/src/level_internal.h208
-rw-r--r--libpoldiff/src/libpoldiff.map50
-rw-r--r--libpoldiff/src/poldiff.c814
-rw-r--r--libpoldiff/src/poldiff_internal.h239
-rw-r--r--libpoldiff/src/range_diff.c420
-rw-r--r--libpoldiff/src/range_internal.h80
-rw-r--r--libpoldiff/src/range_trans_diff.c520
-rw-r--r--libpoldiff/src/range_trans_internal.h124
-rw-r--r--libpoldiff/src/rbac_diff.c1052
-rw-r--r--libpoldiff/src/rbac_internal.h209
-rw-r--r--libpoldiff/src/role_diff.c543
-rw-r--r--libpoldiff/src/role_internal.h121
-rw-r--r--libpoldiff/src/terule_diff.c1329
-rw-r--r--libpoldiff/src/terule_internal.h244
-rw-r--r--libpoldiff/src/type_diff.c662
-rw-r--r--libpoldiff/src/type_internal.h125
-rw-r--r--libpoldiff/src/type_map.c985
-rw-r--r--libpoldiff/src/type_map_internal.h171
-rw-r--r--libpoldiff/src/user_diff.c789
-rw-r--r--libpoldiff/src/user_internal.h121
-rw-r--r--libpoldiff/src/util.c32
-rw-r--r--libpoldiff/src/writing-diffs-HOWTO351
-rw-r--r--libpoldiff/swig/Makefile.am15
-rw-r--r--libpoldiff/swig/java/MANIFEST.MF.in14
-rw-r--r--libpoldiff/swig/java/Makefile.am93
-rw-r--r--libpoldiff/swig/poldiff.i1335
-rw-r--r--libpoldiff/swig/python/Makefile.am39
-rw-r--r--libpoldiff/swig/tcl/Makefile.am37
-rw-r--r--libpoldiff/tests/Makefile.am19
-rw-r--r--libpoldiff/tests/components-tests.c544
-rw-r--r--libpoldiff/tests/components-tests.h49
-rw-r--r--libpoldiff/tests/libpoldiff-tests.c364
-rw-r--r--libpoldiff/tests/libpoldiff-tests.h85
-rw-r--r--libpoldiff/tests/mls-tests.c635
-rw-r--r--libpoldiff/tests/mls-tests.h39
-rw-r--r--libpoldiff/tests/nomls-tests.c139
-rw-r--r--libpoldiff/tests/nomls-tests.h33
-rw-r--r--libpoldiff/tests/policy-defs.h44
-rw-r--r--libpoldiff/tests/rules-tests.c914
-rw-r--r--libpoldiff/tests/rules-tests.h40
72 files changed, 22073 insertions, 0 deletions
diff --git a/libpoldiff/Makefile.am b/libpoldiff/Makefile.am
new file mode 100644
index 0000000..906041a
--- /dev/null
+++ b/libpoldiff/Makefile.am
@@ -0,0 +1,8 @@
+if DO_SWIGIFY
+ MAYBE_SWIG = swig
+endif
+
+SUBDIRS = src include tests $(MAYBE_SWIG)
+
+libpoldiff.a libpoldiff.so:
+ $(MAKE) -C src $@
diff --git a/libpoldiff/include/Makefile.am b/libpoldiff/include/Makefile.am
new file mode 100644
index 0000000..02aa6be
--- /dev/null
+++ b/libpoldiff/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = poldiff
diff --git a/libpoldiff/include/poldiff/Makefile.am b/libpoldiff/include/poldiff/Makefile.am
new file mode 100644
index 0000000..7c5a42c
--- /dev/null
+++ b/libpoldiff/include/poldiff/Makefile.am
@@ -0,0 +1,20 @@
+poldiffdir = $(includedir)/poldiff
+
+poldiff_HEADERS = \
+ poldiff.h \
+ attrib_diff.h \
+ avrule_diff.h \
+ bool_diff.h \
+ cat_diff.h \
+ class_diff.h \
+ component_record.h \
+ level_diff.h \
+ range_diff.h \
+ range_trans_diff.h \
+ rbac_diff.h \
+ role_diff.h \
+ terule_diff.h \
+ user_diff.h \
+ type_diff.h \
+ type_map.h \
+ util.h
diff --git a/libpoldiff/include/poldiff/attrib_diff.h b/libpoldiff/include/poldiff/attrib_diff.h
new file mode 100644
index 0000000..27d1f12
--- /dev/null
+++ b/libpoldiff/include/poldiff/attrib_diff.h
@@ -0,0 +1,130 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in attributes.
+ *
+ * @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 POLDIFF_ATTRIB_DIFF_H
+#define POLDIFF_ATTRIB_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_attrib poldiff_attrib_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for attributes.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_attrib_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of attribute differences from the attribute
+ * difference summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * attribute difference summary.
+ *
+ * @return A vector of elements of type poldiff_attrib_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_attrib_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a attribute.
+ *
+ * @param diff The policy difference structure associated with the
+ * attribute.
+ * @param attrib The attribute from which to generate the string.
+ *
+ * @return A string representation of attribute difference; the
+ * caller is responsible for free()ing this string. On error, return
+ * NULL and set errno.
+ */
+ extern char *poldiff_attrib_to_string(const poldiff_t * diff, const void *attrib);
+
+/**
+ * Get the name of the attribute from an attribute diff.
+ *
+ * @param attrib The attribute from which to get the name.
+ *
+ * @return Name of the attribute on success and NULL on failure; if
+ * the call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_attrib_get_name(const poldiff_attrib_t * attrib);
+
+/**
+ * Get the form of difference from an attribute diff.
+ *
+ * @param attrib The attribute from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_attrib_get_form(const void *attrib);
+
+/**
+ * Get a vector of types added to the attribute.
+ *
+ * @param attrib The attribute diff from which to get the types
+ * vector.
+ *
+ * @return A vector of type names (type char *) that are members of
+ * the attribute in the modified policy. If no types were added the
+ * size of the returned vector will be 0. The caller must not
+ * destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_attrib_get_added_types(const poldiff_attrib_t * attrib);
+
+/**
+ * Get a vector of types removed from the attribute.
+ *
+ * @param attrib The attribute diff from which to get the types
+ * vector.
+ *
+ * @return A vector of type names (type char *) that are members of
+ * the attribute in the original policy. If no types were removed
+ * the size of the returned vector will be 0. The caller must not
+ * destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_attrib_get_removed_types(const poldiff_attrib_t * attrib);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_ATTRIB_DIFF_H */
diff --git a/libpoldiff/include/poldiff/avrule_diff.h b/libpoldiff/include/poldiff/avrule_diff.h
new file mode 100644
index 0000000..454bb9c
--- /dev/null
+++ b/libpoldiff/include/poldiff/avrule_diff.h
@@ -0,0 +1,361 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in av rules
+ * (allow, neverallow, auditallow, dontaudit).
+ *
+ * @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 POLDIFF_AVRULE_DIFF_H
+#define POLDIFF_AVRULE_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_avrule poldiff_avrule_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for all AV rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_avrule_get_stats_allow(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for AV auditallow rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_avrule_get_stats_auditallow(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for AV dontaudit rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_avrule_get_stats_dontaudit(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for AV neverallow rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_avrule_get_stats_neverallow(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of av rule differences from the av rule difference
+ * summary for just allow rules.
+ *
+ * @param diff The policy difference structure associated with the av
+ * rule difference summary.
+ *
+ * @return A vector of elements of type poldiff_avrule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_avrule_vector_allow(const poldiff_t * diff);
+
+/**
+ * Get the vector of av rule differences from the av rule difference
+ * summary for just auditallow rules.
+ *
+ * @param diff The policy difference structure associated with the av
+ * rule difference summary.
+ *
+ * @return A vector of elements of type poldiff_avrule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_avrule_vector_auditallow(const poldiff_t * diff);
+
+/**
+ * Get the vector of av rule differences from the av rule difference
+ * summary for just dontaudit rules.
+ *
+ * @param diff The policy difference structure associated with the av
+ * rule difference summary.
+ *
+ * @return A vector of elements of type poldiff_avrule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_avrule_vector_dontaudit(const poldiff_t * diff);
+
+/**
+ * Get the vector of av rule differences from the av rule difference
+ * summary for just neverallow rules.
+ *
+ * @param diff The policy difference structure associated with the av
+ * rule difference summary.
+ *
+ * @return A vector of elements of type poldiff_avrule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_avrule_vector_neverallow(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * any av rule.
+ *
+ * @param diff The policy difference structure associated with the av
+ * rule.
+ * @param avrule The av rule from which to generate the string.
+ *
+ * @return A string representation of av rule difference; the caller
+ * is responsible for free()ing this string. On error, return NULL
+ * and set errno.
+ */
+ extern char *poldiff_avrule_to_string(const poldiff_t * diff, const void *avrule);
+
+/**
+ * Get the form of difference from any av rule diff.
+ *
+ * @param avrule The av rule from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error.
+ */
+ extern poldiff_form_e poldiff_avrule_get_form(const void *avrule);
+
+/**
+ * Get the type of rule this is from an av rule diff.
+ *
+ * @param avrule The av rule from which to get the rule type.
+ *
+ * @return One of QPOL_RULE_ALLOW etc, suitable for printing via
+ * apol_rule_type_to_str().
+ */
+ extern uint32_t poldiff_avrule_get_rule_type(const poldiff_avrule_t * avrule);
+
+/**
+ * Get the source type from an av rule diff.
+ *
+ * @param avrule The av rule from which to get the type.
+ *
+ * @return A string for the type. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_avrule_get_source_type(const poldiff_avrule_t * avrule);
+
+/**
+ * Get the target type from an av rule diff.
+ *
+ * @param avrule The av rule from which to get the type.
+ *
+ * @return A string for the type. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_avrule_get_target_type(const poldiff_avrule_t * avrule);
+
+/**
+ * Get the object class from an av rule diff.
+ *
+ * @param avrule The av rule from which to get the class.
+ *
+ * @return A string for the class. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_avrule_get_object_class(const poldiff_avrule_t * avrule);
+
+/**
+ * Get the conditional expression from an av rule diff. Note that
+ * this really returns a qpol_cond_t and an apol_policy_t, which may
+ * then be used in other routines such as apol_cond_expr_render().
+ *
+ * @param diff Difference structure from which the rule originated.
+ * @param avrule The av rule from which to get the conditional.
+ * @param cond Reference to the rule's conditional pointer, or NULL
+ * if the rule is not conditional. The caller must not free() this
+ * pointer.
+ * @param which_list Reference to which list the rule belongs, either
+ * 1 if in the true branch, 0 if in false. If the rule is not
+ * conditional then this value will be set to 1.
+ * @param p Reference to the policy from which the conditional
+ * originated, or NULL if the rule is not conditional. The caller
+ * must not destroy this pointer.
+ */
+ extern void poldiff_avrule_get_cond(const poldiff_t * diff, const poldiff_avrule_t * avrule,
+ const qpol_cond_t ** cond, uint32_t * which_list, const apol_policy_t ** p);
+
+/**
+ * Get a vector of permissions unmodified by the av rule. This
+ * vector will be non-empty only if the form is
+ * POLDIFF_FORM_MODIFIED.
+ *
+ * @param avrule The av rule diff from which to get the permissions
+ * vector.
+ *
+ * @return A vector of permissions strings (type char *) that both
+ * policies have. If no permissions are common to both policies then
+ * the size of of the returned vector will be 0. The caller must not
+ * destroy this vector.
+ */
+ extern const apol_vector_t *poldiff_avrule_get_unmodified_perms(const poldiff_avrule_t * avrule);
+
+/**
+ * Get a vector of permissions added to the av rule. If the rule was
+ * added by modified policy then this vector will hold all of the
+ * permissions.
+ *
+ * @param avrule The av rule diff from which to get the permissions
+ * vector.
+ *
+ * @return A vector of permissions strings (type char *) added to the
+ * rule in the modified policy. If no permissions were added the
+ * size of the returned vector will be 0. The caller must not
+ * destroy this vector.
+ */
+ extern const apol_vector_t *poldiff_avrule_get_added_perms(const poldiff_avrule_t * avrule);
+
+/**
+ * Get a vector of permissions removed from the av rule. If the rule
+ * was removed by modified policy then this vector will hold all of
+ * the permissions.
+ *
+ * @param avrule The av rule diff from which to get the permissions
+ * vector.
+ *
+ * @return A vector of permissions strings (type char *) removed from
+ * the rule in the original policy. If no permissions were removed
+ * the size of the returned vector will be 0. The caller must not
+ * destroy this vector.
+ */
+ extern const apol_vector_t *poldiff_avrule_get_removed_perms(const poldiff_avrule_t * avrule);
+
+/**
+ * Get a vector of line numbers (of type unsigned long) for this av rule
+ * difference from the original policy. Note that if the form is
+ * POLDIFF_FORM_ADDED or POLDIFF_FORM_ADD_TYPE then this will return NULL.
+ * Also, if the original policy is a binary policy or line numbers are not yet
+ * enabled then this returns NULL.
+ * @see poldiff_enable_line_numbers() to enable line numbers.
+ *
+ * @param avrule The av rule diff from which to get line numbers.
+ *
+ * @return A vector of line numbers (type unsigned long) for the rule
+ * in the original policy, or NULL if no numbers are available. Do
+ * not destroy or otherwise modify this vector.
+ */
+ extern const apol_vector_t *poldiff_avrule_get_orig_line_numbers(const poldiff_avrule_t * avrule);
+
+/**
+ * Get a vector of line numbers (of type unsigned long) for this av rule
+ * difference from the modified policy. Note that if the form is
+ * POLDIFF_FORM_REMOVED or POLDIFF_FORM_REMOVE_TYPE then this will return
+ * NULL. Also, if the modified policy is a binary policy or line numbers are
+ * not yet enabled then this returns NULL.
+ * @see poldiff_enable_line_numbers() to enable line numbers.
+ *
+ * @param avrule The av rule diff from which to get line numbers.
+ *
+ * @return A vector of line numbers (type unsigned long) for the rule
+ * in the modified policy, or NULL if no numbers are available. Do
+ * not destroy or otherwise modify this vector.
+ */
+ extern const apol_vector_t *poldiff_avrule_get_mod_line_numbers(const poldiff_avrule_t * avrule);
+
+/**
+ * Given an av rule difference and a permission name, return a vector
+ * of all line numbers (of type unsigned long) from the original
+ * policy; these line numbers correspond to rules that contributed to
+ * the av rule difference and have the given permission. Be aware
+ * that the vector could be empty if the permission was not found.
+ * Note that if the form is POLDIFF_FORM_ADDED or
+ * POLDIFF_FORM_ADD_TYPE then this will return NULL. Also, if the
+ * original policy is a binary policy or line numbers are not yet
+ * enabled then this returns NULL.
+ *
+ * @see poldiff_enable_line_numbers() to enable line numbers.
+ *
+ * @param diff Difference object containing policies to query.
+ * @param avrule The av rule diff from which to get line numbers.
+ * @param perm Permission to look up.
+ *
+ * @return A vector of sorted line numbers (type unsigned long) for
+ * the rule in the original policy, or NULL if no numbers are
+ * available. Note that the vector could be empty if the permission
+ * was not found. It is the caller's responsibility to call
+ * apol_vector_destroy() upon the returned value.
+ */
+ extern apol_vector_t *poldiff_avrule_get_orig_line_numbers_for_perm(const poldiff_t * diff, const poldiff_avrule_t * avrule,
+ const char *perm);
+
+/**
+ * Given an av rule difference and a permission name, return a vector
+ * of all line numbers (of type unsigned long) from the modified
+ * policy; these line numbers correspond to rules that contributed to
+ * the av rule difference and have the given permission. Be aware
+ * that the vector could be empty if the permission was not found.
+ * Note that if the form is POLDIFF_FORM_REMOVED or
+ * POLDIFF_FORM_REMOVE_TYPE then this will return NULL. Also, if the
+ * modified policy is a binary policy or line numbers are not yet
+ * enabled then this returns NULL.
+ *
+ * @see poldiff_enable_line_numbers() to enable line numbers.
+ *
+ * @param diff Difference object containing policies to query.
+ * @param avrule The av rule diff from which to get line numbers.
+ * @param perm Permission to look up.
+ *
+ * @return A vector of sorted line numbers (type unsigned long) for
+ * the rule in the modified policy, or NULL if no numbers are
+ * available. Note that the vector could be empty if the permission
+ * was not found. It is the caller's responsibility to call
+ * apol_vector_destroy() upon the returned value.
+ */
+ extern apol_vector_t *poldiff_avrule_get_mod_line_numbers_for_perm(const poldiff_t * diff, const poldiff_avrule_t * avrule,
+ const char *perm);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_AVRULE_DIFF_H */
diff --git a/libpoldiff/include/poldiff/bool_diff.h b/libpoldiff/include/poldiff/bool_diff.h
new file mode 100644
index 0000000..e64d33c
--- /dev/null
+++ b/libpoldiff/include/poldiff/bool_diff.h
@@ -0,0 +1,146 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in booleans.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Randy Wicks rwicks@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 POLDIFF_BOOL_DIFF_H
+#define POLDIFF_BOOL_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_bool poldiff_bool_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for bools.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is as
+ * follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * form POLDIFF_FORM_ADD_TYPE, and number of
+ * POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_bool_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of bool differences from the boolean difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * bool difference summary.
+ *
+ * @return A vector of elements of type poldiff_bool_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_bool_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a bool.
+ *
+ * @param diff The policy difference structure associated with the bool.
+ * @param item The bool from which to generate the string.
+ *
+ * @return A string representation of bool difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_bool_to_string(const poldiff_t * diff, const void *boolean);
+
+/**
+ * Get the number of added bools from a policy difference
+ * structure.
+ *
+ * @param diff The policy difference structure from which to get the
+ * number of added bools.
+ *
+ * @return The number of added bools or 0 if not yet run. (The
+ * number of differences could also be zero.)
+ */
+ extern size_t poldiff_get_num_added_bools(const poldiff_t * diff);
+
+/**
+ * Get the number of removed bools from a policy difference
+ * structure.
+ *
+ * @param diff The policy difference structure from which to get the
+ * number of removed bools.
+ *
+ * @return The number of removed bools or 0 if not yet run. (The
+ * number of differences could also be zero.)
+ */
+ extern size_t poldiff_get_num_removed_bools(const poldiff_t * diff);
+
+/**
+ * Get the number of modified bools from a policy difference
+ * structure.
+ *
+ * @param diff The policy difference structure from which to get the
+ * number of modified bools.
+ *
+ * @return The number of modified bools or 0 if not yet run. (The
+ * number of differences could also be zero.)
+ */
+ extern size_t poldiff_get_num_modified_bools(const poldiff_t * diff);
+
+/**
+ * Get the name of the bool from a bool diff.
+ *
+ * @param diff The policy difference structure associated with the
+ * bool diff.
+ * @param cls The bool from which to get the name.
+ *
+ * @return Name of the bool on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_bool_get_name(const poldiff_bool_t * boolean);
+
+/**
+ * Get the form of difference from a bool diff.
+ *
+ * @param diff The policy difference structure associated with the
+ * bool diff.
+ *
+ * @param cls The bool from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_bool_get_form(const void *boolean);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_BOOL_DIFF_H */
diff --git a/libpoldiff/include/poldiff/cat_diff.h b/libpoldiff/include/poldiff/cat_diff.h
new file mode 100644
index 0000000..c845639
--- /dev/null
+++ b/libpoldiff/include/poldiff/cat_diff.h
@@ -0,0 +1,103 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in categories.
+ *
+ * @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 POLDIFF_CAT_DIFF_H
+#define POLDIFF_CAT_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_cat poldiff_cat_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for categories.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_cat_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of user differences from the category difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * category difference summary.
+ *
+ * @return A vector of elements of type poldiff_cat_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_cat_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a category.
+ *
+ * @param diff The policy difference structure associated with the category.
+ * @param cat The category from which to generate the string.
+ *
+ * @return A string representation of category difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_cat_to_string(const poldiff_t * diff, const void *cat);
+
+/**
+ * Get the name of the category from a category diff.
+ *
+ * @param cat The category from which to get the name.
+ *
+ * @return Name of the category on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_cat_get_name(const poldiff_cat_t * cat);
+
+/**
+ * Get the form of difference from a category diff.
+ *
+ * @param cat The category from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_cat_get_form(const void *cat);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_CAT_DIFF_H */
diff --git a/libpoldiff/include/poldiff/class_diff.h b/libpoldiff/include/poldiff/class_diff.h
new file mode 100644
index 0000000..0d89924
--- /dev/null
+++ b/libpoldiff/include/poldiff/class_diff.h
@@ -0,0 +1,222 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in classes
+ * and commons.
+ *
+ * @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 POLDIFF_CLASS_DIFF_H
+#define POLDIFF_CLASS_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+/******************** object classes ********************/
+
+ typedef struct poldiff_class poldiff_class_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for object classes.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_class_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of class differences from the class difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * class difference summary.
+ *
+ * @return A vector of elements of type poldiff_class_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_class_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * an object class.
+ *
+ * @param diff The policy difference structure associated with the class.
+ * @param cls The class from which to generate the string.
+ *
+ * @return A string representation of class difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_class_to_string(const poldiff_t * diff, const void *cls);
+
+/**
+ * Get the name of the class from a class diff.
+ *
+ * @param cls The class from which to get the name.
+ *
+ * @return Name of the class on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_class_get_name(const poldiff_class_t * cls);
+
+/**
+ * Get the form of difference from a class diff.
+ *
+ * @param cls The class from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_class_get_form(const void *cls);
+
+/**
+ * Get a vector of permissions added to the class.
+ *
+ * @param cls The class diff from which to get the permission vector.
+ *
+ * @return A vector of permission names (type char *) that are
+ * assigned to the class in the modified policy. If no permissions
+ * were added the size of the returned vector will be 0. The caller
+ * must not destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_class_get_added_perms(const poldiff_class_t * cls);
+
+/**
+ * Get a vector of permissions removed from the class.
+ *
+ * @param cls The class diff from which to get the permission vector.
+ *
+ * @return A vector of permission names (type char *) that are
+ * assigned to the class in the original policy. If no permissions
+ * were removed the size of the returned vector will be 0. The
+ * caller must not destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_class_get_removed_perms(const poldiff_class_t * cls);
+
+/******************** common classes ********************/
+
+ typedef struct poldiff_common poldiff_common_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for common classes.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_common_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of commons differences from the commons difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * commons difference summary.
+ *
+ * @return A vector of elements of type poldiff_common_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_common_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a common class.
+ *
+ * @param diff The policy difference structure associated with the
+ * common.
+ * @param common The common from which to generate the string.
+ *
+ * @return A string representation of common difference; the caller
+ * is responsible for free()ing this string. On error, return NULL
+ * and set errno.
+ */
+ extern char *poldiff_common_to_string(const poldiff_t * diff, const void *common);
+
+/**
+ * Get the name of the common from a common diff.
+ *
+ * @param common The common from which to get the name.
+ *
+ * @return Name of the common on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_common_get_name(const poldiff_common_t * common);
+
+/**
+ * Get the form of difference from a common diff.
+ *
+ * @param common The common from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_common_get_form(const void *common);
+
+/**
+ * Get a vector of permissions added to the common.
+ *
+ * @param common The common diff from which to get the permission
+ * vector.
+ *
+ * @return A vector of permission names (type char *) that are
+ * assigned to the common in the modified policy. If no permissions
+ * were added the size of the returned vector will be 0. The caller
+ * must not destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_common_get_added_perms(const poldiff_common_t * common);
+
+/**
+ * Get a vector of permissions removed from the common.
+ *
+ * @param common The common diff from which to get the permission
+ * vector.
+ *
+ * @return A vector of permission names (type char *) that are
+ * assigned to the common in the original policy. If no permissions
+ * were removed the size of the returned vector will be 0. The
+ * caller must not destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_common_get_removed_perms(const poldiff_common_t * common);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_CLASS_DIFF_H */
diff --git a/libpoldiff/include/poldiff/component_record.h b/libpoldiff/include/poldiff/component_record.h
new file mode 100644
index 0000000..0104845
--- /dev/null
+++ b/libpoldiff/include/poldiff/component_record.h
@@ -0,0 +1,159 @@
+/**
+ * @file
+ * Typedefs to aid declaring function pointers for callbacks
+ * extracted from component records.
+ *
+ * This file also declares functions to extract the callbacks for
+ * component records. This implements a form of polymorphism so that
+ * one can operate on component records and not care about the
+ * library dependent implementation.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Mark Goldman mgoldman@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 POLDIFF_COMPONENT_RECORD_H
+#define POLDIFF_COMPONENT_RECORD_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * Callback function signature for getting an array of statistics for the
+ * number of differences of each form for a given item.
+ * @param diff The policy difference structure from which to get the stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is as follows:
+ * number of items of form POLDIFF_FORM_ADDED, number of POLDIFF_FORM_REMOVED,
+ * number of POLDIFF_FORM_MODIFIED, number of form POLDIFF_FORM_ADD_TYPE, and
+ * number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ typedef void (*poldiff_get_item_stats_fn_t) (const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Callback function signature for getting a vector of all result
+ * items that were created during a call to poldiff_do_item_diff().
+ * @param diff Policy diff structure containing results.
+ * @return A vector of result items, which the caller may not modify
+ * or destroy. Upon error, return null and set errno.
+ */
+ typedef const apol_vector_t *(*poldiff_get_result_items_fn_t) (const poldiff_t * diff);
+
+/**
+ * Callback function signature for getting the form of difference for
+ * a result item.
+ * @param diff The policy difference structure associated with the item.
+ * @param item The item from which to get the form.
+ * @return One of the POLDIFF_FORM_* enumeration.
+ */
+ typedef poldiff_form_e(*poldiff_item_get_form_fn_t) (const void *item);
+
+/**
+ * Callback function signature for obtaining a newly allocated string
+ * representation of a difference item.
+ * @param diff The policy difference structure associated with the item.
+ * @param item The item from which to generate the string.
+ * @return Expected return value from this function is a newly allocated
+ * string representation of the item or null on error; if the call fails,
+ * it is expected to set errno.
+ */
+ typedef char *(*poldiff_item_to_string_fn_t) (const poldiff_t * diff, const void *item);
+
+ typedef struct poldiff_component_record poldiff_component_record_t;
+
+/**
+ * Get the poldiff_component_record_t for a particular policy
+ * component. Consult this record for function pointers, so as to
+ * achieve a limited form of polymorphism.
+ *
+ * @param which Flag (as defined in <poldiff/poldiff.h>) indicating
+ * which component to look up.
+ * @return A poldiff_component_record_t associated with the component
+ * or NULL if not found.
+ */
+ extern const poldiff_component_record_t *poldiff_get_component_record(uint32_t which);
+
+/**
+ * Get the function that will return the form from a
+ * poldiff_component_record_t.
+ *
+ * @param comp Pointer to the component to extract the named virtual
+ * function.
+ *
+ * @return Function pointer relating to the passed in record key, or
+ * NULL upon error.
+ */
+ extern poldiff_item_get_form_fn_t poldiff_component_record_get_form_fn(const poldiff_component_record_t * comp);
+
+/**
+ * Get the function that will return the to_string from a
+ * poldiff_component_record_t.
+ *
+ * @param diff Pointer to the component to extract the named virtual
+ * function.
+ *
+ * @return Function pointer relating to the passed in record key, or
+ * NULL upon error.
+ */
+ extern poldiff_item_to_string_fn_t poldiff_component_record_get_to_string_fn(const poldiff_component_record_t * diff);
+
+/**
+ * Get the function that will return the item_stats from a
+ * poldiff_component_record_t.
+ *
+ * @param diff Pointer to the component to extract the named virtual
+ * function.
+ *
+ * @return Function pointer relating to the passed in record key, or
+ * NULL upon error.
+ */
+ extern poldiff_get_item_stats_fn_t poldiff_component_record_get_stats_fn(const poldiff_component_record_t * diff);
+
+/**
+ * Get the function that will return the results from a
+ * poldiff_component_record_t.
+ *
+ * @param diff Pointer to the component to extract the named virtual
+ * function.
+ *
+ * @return Function pointer relating to the passed in record key, or
+ * NULL upon error.
+ */
+ extern poldiff_get_result_items_fn_t poldiff_component_record_get_results_fn(const poldiff_component_record_t * diff);
+
+/**
+ * Get the function that will return the label from a
+ * poldiff_component_record_t. This label describes the policy
+ * component (e.g., "attribute" or "AVrule dontaudit").
+ *
+ * @param diff Pointer to the component to extract named the label.
+ *
+ * @return Label describing the policy component record. Do not
+ * modify this string.
+ */
+ extern const char *poldiff_component_record_get_label(const poldiff_component_record_t * diff);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libpoldiff/include/poldiff/level_diff.h b/libpoldiff/include/poldiff/level_diff.h
new file mode 100644
index 0000000..e9eba23
--- /dev/null
+++ b/libpoldiff/include/poldiff/level_diff.h
@@ -0,0 +1,159 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in levels.
+ *
+ * @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 POLDIFF_LEVEL_DIFF_H
+#define POLDIFF_LEVEL_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_level poldiff_level_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for levels.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_level_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of level differences from the level difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * level difference summary.
+ *
+ * @return A vector of elements of type poldiff_level_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_level_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a level.
+ *
+ * @param diff The policy difference structure associated with the level.
+ * @param level The level from which to generate the string.
+ *
+ * @return A string representation of level difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_level_to_string(const poldiff_t * diff, const void *level);
+
+/**
+ * Allocate and return a string rendering of a poldiff_level_t,
+ * suitable for embedding within some other component's to_string
+ * function (e.g., a user's default level).
+ *
+ * @param diff Poldiff object, for error handling.
+ * @param level Level diff object to render.
+ *
+ * @return String rendering of level, or NULL upon error. Caller must
+ * free() string afterwards.
+ */
+ char *poldiff_level_to_string_brief(const poldiff_t * diff, const poldiff_level_t * level);
+
+/**
+ * Get the name of the level (i.e., the sensitivity) from a level diff.
+ *
+ * @param level The level from which to get the name.
+ *
+ * @return Name of the level on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_level_get_name(const poldiff_level_t * level);
+
+/**
+ * Get the form of difference from a level diff.
+ *
+ * @param level The level from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_level_get_form(const void *level);
+
+/**
+ * Get a vector of unmodified categories from the level. These will
+ * be sorted in the same order as given by the original policy.
+ *
+ * @param level The level diff from which to get the category vector.
+ *
+ * @return A vector of category names (type char *) that are assigned to
+ * the level in the original policy. If no categories were removed the
+ * size of the returned vector will be 0. The caller must not
+ * destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_level_get_unmodified_cats(const poldiff_level_t * level);
+
+/**
+ * Get a vector of categories added to the level. These will be
+ * sorted in the same order as given by the modified policy. If the
+ * level was added by the modified policy then this vector will hold
+ * all of the categories.
+ *
+ * @param level The level diff from which to get the categories.
+ *
+ * @return A vector of category names (type char *) that are assigned
+ * to the level in the modified policy. If no categories were added
+ * the size of the returned vector will be 0. The caller must not
+ * modify this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_level_get_added_cats(const poldiff_level_t * level);
+
+/**
+ * Get a vector of categories removed from the level. These will be
+ * sorted in the same order as given by the original policy. If the
+ * level was removed by the modified policy then this vector will
+ * hold all of the categories.
+ *
+ * @param level The level diff from which to get the category vector.
+ *
+ * @return A vector of category names (type char *) that are assigned to
+ * the level in the original policy. If no categories were removed the
+ * size of the returned vector will be 0. The caller must not
+ * destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_level_get_removed_cats(const poldiff_level_t * level);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_LEVEL_DIFF_H */
diff --git a/libpoldiff/include/poldiff/poldiff.h b/libpoldiff/include/poldiff/poldiff.h
new file mode 100644
index 0000000..9047600
--- /dev/null
+++ b/libpoldiff/include/poldiff/poldiff.h
@@ -0,0 +1,218 @@
+/**
+ * @file
+ * Public interface for computing semantic policy differences
+ * between two policies. The user loads two policies, the "original"
+ * and "modified" policies, and then calls poldiff_create() to obtain
+ * a poldiff object. Next call poldiff_run() to actually execute the
+ * differencing algorithm. Results are retrieved via
+ * poldiff_get_type_vector(), poldiff_get_avrule_vector(), and so
+ * forth.
+ *
+ * @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 POLDIFF_POLDIFF_H
+#define POLDIFF_POLDIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/policy.h>
+#include <apol/policy-query.h>
+#include <apol/vector.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+ typedef struct poldiff poldiff_t;
+
+/**
+ * Form of a difference. This enumeration describes the kind of change
+ * in a policy component or rule from policy1 to policy2.
+ * Differences can be additions (item present only in policy2),
+ * removals (item present only in policy1) or a modification
+ * (item present in both policies with different semantic meaning).
+ * For rules there are two more options - added or removed due to a
+ * type being added or removed; these forms differentiate these cases
+ * from those of added/removed rules where the types exist in both policies.
+ */
+ typedef enum poldiff_form
+ {
+ /** only for error conditions */
+ POLDIFF_FORM_NONE,
+ /** item was added - only in policy 2 */
+ POLDIFF_FORM_ADDED,
+ /** item was removed - only in policy 1 */
+ POLDIFF_FORM_REMOVED,
+ /** item was modified - in both policies but with different meaning */
+ POLDIFF_FORM_MODIFIED,
+ /** item was added due to an added type - for rules only */
+ POLDIFF_FORM_ADD_TYPE,
+ /** item was removed due to a removed type - for rules only */
+ POLDIFF_FORM_REMOVE_TYPE
+ } poldiff_form_e;
+
+ typedef void (*poldiff_handle_fn_t) (void *arg, const poldiff_t * diff, int level, const char *fmt, va_list va_args);
+
+#include <poldiff/attrib_diff.h>
+#include <poldiff/avrule_diff.h>
+#include <poldiff/cat_diff.h>
+#include <poldiff/bool_diff.h>
+#include <poldiff/class_diff.h>
+#include <poldiff/level_diff.h>
+#include <poldiff/range_diff.h>
+#include <poldiff/range_trans_diff.h>
+#include <poldiff/rbac_diff.h>
+#include <poldiff/role_diff.h>
+#include <poldiff/terule_diff.h>
+#include <poldiff/type_diff.h>
+#include <poldiff/user_diff.h>
+#include <poldiff/type_map.h>
+#include <poldiff/util.h>
+
+/* NOTE: while defined OCONS are not currently supported */
+#define POLDIFF_DIFF_CLASSES 0x00000001U
+#define POLDIFF_DIFF_COMMONS 0x00000002U
+#define POLDIFF_DIFF_TYPES 0x00000004U
+#define POLDIFF_DIFF_ATTRIBS 0x00000008U
+#define POLDIFF_DIFF_ROLES 0x00000010U
+#define POLDIFF_DIFF_USERS 0x00000020U
+#define POLDIFF_DIFF_BOOLS 0x00000040U
+#define POLDIFF_DIFF_LEVELS 0x00000080U
+#define POLDIFF_DIFF_CATS 0x00000100U
+#define POLDIFF_DIFF_ROLE_ALLOWS 0x00000800U
+#define POLDIFF_DIFF_ROLE_TRANS 0x00001000U
+#define POLDIFF_DIFF_RANGE_TRANS 0x00002000U
+#define POLDIFF_DIFF_AVALLOW 0x10000000U
+#define POLDIFF_DIFF_AVAUDITALLOW 0x20000000U
+#define POLDIFF_DIFF_AVDONTAUDIT 0x40000000U
+#define POLDIFF_DIFF_AVNEVERALLOW 0x80000000U
+#define POLDIFF_DIFF_TECHANGE 0x01000000U
+#define POLDIFF_DIFF_TEMEMBER 0x02000000U
+#define POLDIFF_DIFF_TETRANS 0x04000000U
+
+#define POLDIFF_DIFF_TERULES_COMPAT 0x00000400U /**< deprecated */
+#define POLDIFF_DIFF_AVRULES_COMPAT 0x00000200U /**< deprecated */
+
+#define POLDIFF_DIFF_AVRULES (POLDIFF_DIFF_AVALLOW | POLDIFF_DIFF_AVNEVERALLOW | POLDIFF_DIFF_AVAUDITALLOW | POLDIFF_DIFF_AVDONTAUDIT)
+#define POLDIFF_DIFF_TERULES (POLDIFF_DIFF_TEMEMBER | POLDIFF_DIFF_TECHANGE | POLDIFF_DIFF_TETRANS)
+/*
+ * Add ocons here and modify POLDIFF_DIFF_OCONS below
+ * #define POLDIFF_DIFF_ *
+ */
+#define POLDIFF_DIFF_SYMBOLS (POLDIFF_DIFF_CLASSES|POLDIFF_DIFF_COMMONS|POLDIFF_DIFF_TYPES|POLDIFF_DIFF_ATTRIBS|POLDIFF_DIFF_ROLES|POLDIFF_DIFF_USERS|POLDIFF_DIFF_BOOLS)
+#define POLDIFF_DIFF_RULES (POLDIFF_DIFF_AVRULES|POLDIFF_DIFF_TERULES|POLDIFF_DIFF_ROLE_ALLOWS|POLDIFF_DIFF_ROLE_TRANS)
+#define POLDIFF_DIFF_RBAC (POLDIFF_DIFF_ROLES|POLDIFF_DIFF_ROLE_ALLOWS|POLDIFF_DIFF_ROLE_TRANS)
+#define POLDIFF_DIFF_MLS (POLDIFF_DIFF_LEVELS|POLDIFF_DIFF_CATS|POLDIFF_DIFF_RANGE_TRANS)
+#define POLDIFF_DIFF_OCONS 0
+#define POLDIFF_DIFF_REMAPPED (POLDIFF_DIFF_TYPES|POLDIFF_DIFF_ATTRIBS|POLDIFF_DIFF_AVRULES|POLDIFF_DIFF_TERULES|POLDIFF_DIFF_ROLES|POLDIFF_DIFF_ROLE_TRANS|POLDIFF_DIFF_RANGE_TRANS|POLDIFF_DIFF_OCONS)
+#define POLDIFF_DIFF_ALL (POLDIFF_DIFF_SYMBOLS|POLDIFF_DIFF_RULES|POLDIFF_DIFF_MLS|POLDIFF_DIFF_OCONS)
+
+/**
+ * Allocate and initialize a new policy difference structure. This
+ * function takes ownership of the supplied policies and will handle
+ * their destruction upon poldiff_destroy(). The poldiff object will
+ * be responsible for rebuilding the policy (such as if neverallows
+ * are requested). It is still safe to access elements within the
+ * policies, but avoid making changes to the policy while the poldiff
+ * object still exists.
+ * @param orig_policy The original policy.
+ * @param mod_policy The new (modified) policy.
+ * @param fn Function to be called by the error handler. If NULL
+ * then write messages to standard error.
+ * @param callback_arg Argument for the callback.
+ * @return a newly allocated and initialized difference structure or
+ * NULL on error; if the call fails, errno will be set.
+ * The caller is responsible for calling poldiff_destroy() to free
+ * memory used by this structure.
+ */
+ extern poldiff_t *poldiff_create(apol_policy_t * orig_policy,
+ apol_policy_t * mod_policy, poldiff_handle_fn_t fn, void *callback_arg);
+
+/**
+ * Free all memory used by a policy difference structure and set it to NULL.
+ * @param diff Reference pointer to the difference structure to destroy.
+ * This pointer will be set to NULL. (If already NULL, function is a no-op.)
+ */
+ extern void poldiff_destroy(poldiff_t ** diff);
+
+/**
+ * Run the difference algorithm for the selected policy components/rules.
+ * @param diff The policy difference structure for which to compute
+ * the differences.
+ * @param flags Bit-wise or'd set of POLDIFF_DIFF_* from above indicating
+ * the components and rules for which to compute the difference.
+ * If an item has already been computed the flag for that item is ignored.
+ * @return 0 on success or < 0 on error; if the call fails, errno will
+ * be set and the only defined operation on the difference structure is
+ * poldiff_destroy().
+ */
+ extern int poldiff_run(poldiff_t * diff, uint32_t flags);
+
+/**
+ * Determine if a particular policy component/rule diff was actually
+ * run yet or not.
+ * @param diff The policy difference structure for which to compute
+ * the differences.
+ * @param flags Bit-wise or'd set of POLDIFF_DIFF_* from above indicating
+ * which components/rules diffs were run.
+ * @return 1 if all indicated diffs were run, 0 if any were not, < 0
+ * on error.
+ */
+ extern int poldiff_is_run(const poldiff_t * diff, uint32_t flags);
+
+/**
+ * Get a total of the differences of each form for a given item (or set
+ * of items).
+ * @param diff The policy difference structure from which to get the stats.
+ * @param flags Bit-wise or'd set of POLDIFF_DIFF_* from above indicating
+ * the items for which to get the total differences. If more that one bit
+ * is set differences of the same form are totaled for all specified items.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is as follows:
+ * number of items of form POLDIFF_FORM_ADDED, number of POLDIFF_FORM_REMOVED,
+ * number of POLDIFF_FORM_MODIFIED, number of form POLDIFF_FORM_ADD_TYPE, and
+ * number of POLDIFF_FORM_REMOVE_TYPE.
+ * @return 0 on success and < 0 on error; if the call fails, errno will be set.
+ */
+ extern int poldiff_get_stats(const poldiff_t * diff, uint32_t flags, size_t stats[5]);
+
+/**
+ * Enable line numbers for all rule differences. If not called, line
+ * numbers will not be available when displaying differences. This
+ * function is safe to call multiple times and will have no effect
+ * after the first time. It also has no effect if one policy (or
+ * both of them) does not support line numbers. Be aware that line
+ * numbers will need to be re-enabled each time poldiff_run() is
+ * called.
+ *
+ * @param diff The policy difference structure.
+ *
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and the difference structure should be destroyed.
+ */
+ extern int poldiff_enable_line_numbers(poldiff_t * diff);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_POLDIFF_H */
diff --git a/libpoldiff/include/poldiff/range_diff.h b/libpoldiff/include/poldiff/range_diff.h
new file mode 100644
index 0000000..fcdd846
--- /dev/null
+++ b/libpoldiff/include/poldiff/range_diff.h
@@ -0,0 +1,129 @@
+/**
+ * @file
+ * Public interface for returning the differences in MLS ranges.
+ * Obtain a range difference object from its respective policy
+ * component (e.g., a user's assigned range). The individual level
+ * difference querying functions are in the level_diff.h header.
+ *
+ * @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 POLDIFF_RANGE_DIFF_H
+#define POLDIFF_RANGE_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/mls-query.h>
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_range poldiff_range_t;
+
+/**
+ * Allocate and return a string that represents the differences
+ * encoded by the given range. The returned string is suitable for
+ * embedding within another item's to_string() display.
+ *
+ * @param diff Poldiff diff structure containing policies.
+ * @param range Range object to render.
+ *
+ * @return Rendered string, or NULL upon error. Caller must free()
+ * string afterwards.
+ */
+ char *poldiff_range_to_string_brief(const poldiff_t * diff, const poldiff_range_t * range);
+
+/**
+ * Get the vector of level differences from a range diffence object.
+ *
+ * @param range Range object to query.
+ *
+ * @return A vector of elements of type poldiff_level_t, or NULL on
+ * error. The caller should <b>not</b> modify the returned vector.
+ */
+ extern apol_vector_t *poldiff_range_get_levels(const poldiff_range_t * range);
+
+/**
+ * Get the original item's range. This could represent a user's
+ * original assigned range or the original target range for a
+ * range_transition. If there was no original range (such as for
+ * items that are added) then this returns NULL.
+ *
+ * @param range Range object to query.
+ *
+ * @return Original range, or NULL upon error or no range available.
+ * The caller should <b>not</b> modify the returned object.
+ */
+ extern const apol_mls_range_t *poldiff_range_get_original_range(const poldiff_range_t * range);
+
+/**
+ * Get the modified item's range. This could represent a user's
+ * modified assigned range or the modified target range for a
+ * range_transition. If there was no original range (such as for
+ * items that are removed) then this returns NULL.
+ *
+ * @param range Range object to query.
+ *
+ * @return Modified range, or NULL upon error or no range available.
+ * The caller should <b>not</b> modify the returned object.
+ */
+ extern const apol_mls_range_t *poldiff_range_get_modified_range(const poldiff_range_t * range);
+
+/**
+ * Get the vector of categories added to the minimum set from a range
+ * diffence object.
+ *
+ * @param range Range object to query.
+ *
+ * @return A vector of elements of type string, or NULL on
+ * error. The caller should <b>not</b> modify the returned vector.
+ */
+
+ extern apol_vector_t *poldiff_range_get_min_added_cats(const poldiff_range_t * range);
+
+/**
+ * Get the vector of categories removed from the minimum set from a
+ * range diffence object.
+ *
+ * @param range Range object to query.
+ *
+ * @return A vector of elements of type string, or NULL on
+ * error. The caller should <b>not</b> modify the returned vector.
+ */
+ extern apol_vector_t *poldiff_range_get_min_removed_cats(const poldiff_range_t * range);
+
+/**
+ * Get the vector of unmodified categories of the minimum set from a
+ * range diffence object.
+ *
+ * @param range Range object to query.
+ *
+ * @return A vector of elements of type string, or NULL on
+ * error. The caller should <b>not</b> modify the returned vector.
+ */
+ extern apol_vector_t *poldiff_range_get_min_unmodified_cats(const poldiff_range_t * range);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_RANGE_DIFF_H */
diff --git a/libpoldiff/include/poldiff/range_trans_diff.h b/libpoldiff/include/poldiff/range_trans_diff.h
new file mode 100644
index 0000000..ee6dc5b
--- /dev/null
+++ b/libpoldiff/include/poldiff/range_trans_diff.h
@@ -0,0 +1,140 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in range
+ * transition rules.
+ *
+ * @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 POLDIFF_RANGETRANS_DIFF_H
+#define POLDIFF_RANGETRANS_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/mls-query.h>
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_range_trans poldiff_range_trans_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for range transition rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_range_trans_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of range transition differences from the policy
+ * difference structure.
+ *
+ * @param diff The policy difference structure from which to get the
+ * differences.
+ *
+ * @return A vector of elements of type poldiff_range_trans_t, or
+ * NULL on error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_range_trans_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a range transition rule.
+ *
+ * @param diff The policy difference structure associated with the rule.
+ * @param range_trans The range transition diff from which to
+ * generate the string.
+ *
+ * @return A string representation of the rule difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_range_trans_to_string(const poldiff_t * diff, const void *range_trans);
+
+/**
+ * Get the name of the source type from a range transition diff.
+ *
+ * @param range_trans The rule from which to get the source type.
+ *
+ * @return Name of the source type on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_range_trans_get_source_type(const poldiff_range_trans_t * range_trans);
+
+/**
+ * Get the name of the target type from a range transition diff.
+ *
+ * @param range_trans The rule from which to get the target type.
+ *
+ * @return Name of the target type on success and NULL on failure; if
+ * the call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_range_trans_get_target_type(const poldiff_range_trans_t * range_trans);
+
+/**
+ * Get the name of the target object class from a range transition
+ * diff.
+ *
+ * @param range_trans The rule from which to get the target class.
+ *
+ * @return Name of the target class on success and NULL on failure;
+ * if the call fails, errno will be set. The caller should not free
+ * the returned string.
+ */
+ extern const char *poldiff_range_trans_get_target_class(const poldiff_range_trans_t * range_trans);
+
+/**
+ * Get the change in target range from a range transition diff.
+ *
+ * @param range_trans The rule from which to get the target range.
+ *
+ * @return Rule's target range on success, or NULL upon error or if
+ * there is no difference in range. Do not modify the returned value.
+ */
+ extern const poldiff_range_t *poldiff_range_trans_get_range(const poldiff_range_trans_t * range_trans);
+
+/**
+ * Get the form of difference from a range transition diff.
+ *
+ * @param range_trans The range transition rule from which to get the
+ * difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_range_trans_get_form(const void *range_trans);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_RANGETRANS_DIFF_H */
diff --git a/libpoldiff/include/poldiff/rbac_diff.h b/libpoldiff/include/poldiff/rbac_diff.h
new file mode 100644
index 0000000..200beb3
--- /dev/null
+++ b/libpoldiff/include/poldiff/rbac_diff.h
@@ -0,0 +1,251 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in role
+ * allow rules and role_transition 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 POLDIFF_RBAC_DIFF_H
+#define POLDIFF_RBAC_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_role_allow poldiff_role_allow_t;
+ typedef struct poldiff_role_trans poldiff_role_trans_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for role allow rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_role_allow_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of role allow differences from the policy difference
+ * structure.
+ *
+ * @param diff The policy difference structure from which to get the
+ * differences.
+ *
+ * @return A vector of elements of type poldiff_role_allow_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_role_allow_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a role allow rule.
+ *
+ * @param diff The policy difference structure associated with the rule.
+ * @param role_allow The role from which to generate the string.
+ *
+ * @return A string representation of the rule difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_role_allow_to_string(const poldiff_t * diff, const void *role_allow);
+
+/**
+ * Get the name of the source role from a role allow diff.
+ *
+ * @param role_allow The rule allow from which to get the source role name.
+ *
+ * @return Name of the source role on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_role_allow_get_name(const poldiff_role_allow_t * role_allow);
+
+/**
+ * Get the form of difference from a role allow diff.
+ *
+ * @param role_allow The role allow rule from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_role_allow_get_form(const void *role_allow);
+
+/**
+ * Get a vector of roles unmodified by the role allow rule. The
+ * vector will be non-empty only if the form is
+ * POLDIFF_FORM_MODIFIED.
+ *
+ * @param role_allow The role allow diff from which to get the roles
+ * vector.
+ *
+ * @return A vector of role names (type char *) that are in both
+ * policies. If no roles are common to both policies then the size
+ * of the returned vector will be 0. The caller must not destroy
+ * this vector. The caller must not destroy this vector.
+ */
+ extern const apol_vector_t *poldiff_role_allow_get_unmodified_roles(const poldiff_role_allow_t * role_allow);
+
+/**
+ * Get a vector of roles added to the role allow rule. If the role
+ * allow was added by the modified policy then this vector will hold
+ * all of the roles.
+ *
+ * @param role_allow The role allow diff from which to get the roles
+ * vector.
+ *
+ * @return A vector of role names (type char *) that are allowed to
+ * the role in the modified policy. If no roles were added the size
+ * of the returned vector will be 0. The caller must not destroy
+ * this vector.
+ */
+ extern const apol_vector_t *poldiff_role_allow_get_added_roles(const poldiff_role_allow_t * role_allow);
+
+/**
+ * Get a vector of roles removed from the role allow rule. If the
+ * role allow was removed by the modified policy then this vector
+ * will hold all of the roles.
+ *
+ * @param role_allow The role allow diff from which to get the roles
+ * vector.
+ *
+ * @return A vector of role names (type char *) that are allowed to
+ * the role in the original policy. If no roles were removed the
+ * size of the returned vector will be 0. The caller must not
+ * destroy this vector.
+ */
+ extern const apol_vector_t *poldiff_role_allow_get_removed_roles(const poldiff_role_allow_t * role_allow);
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for role_transition rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_role_trans_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of role_transition differences from the policy difference
+ * structure.
+ *
+ * @param diff The policy difference structure from which to get the
+ * differences.
+ *
+ * @return A vector of elements of type poldiff_role_trans_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_role_trans_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a role_transition rule.
+ *
+ * @param diff The policy difference structure associated with the rule.
+ * @param role_trans The rule from which to generate the string.
+ *
+ * @return A string representation of the rule difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_role_trans_to_string(const poldiff_t * diff, const void *role_trans);
+
+/**
+ * Get the name of the source role from a role_transition difference.
+ *
+ * @param role_trans The rule from which to get the source role.
+ *
+ * @return Name of the source role on success and NULL on failure;
+ * if the call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_role_trans_get_source_role(const poldiff_role_trans_t * role_trans);
+
+/**
+ * Get the name of the target type from a role_transition difference.
+ *
+ * @param role_trans The rule from which to get the target type.
+ *
+ * @return Name of the target type on success and NULL on failure;
+ * if the call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_role_trans_get_target_type(const poldiff_role_trans_t * role_trans);
+
+/**
+ * Get the form of difference from a role_transition diff.
+ *
+ * @param role_trans The role_transition rule from which to get the
+ * difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_role_trans_get_form(const void *role_trans);
+
+/**
+ * Get the original default type from a role_transition diff. Note that
+ * if this rule was added (form POLDIFF_FORM_ADDED or POLDIFF_FORM_ADD_TYPE)
+ * then the return value will be NULL.
+ *
+ * @param role_trans The role_transition rule from which to get the
+ * original default role.
+ *
+ * @return Name of the original default role. If there was no original role or
+ * upon error then return NULL. The caller should not free the returned
+ * string.
+ */
+ extern const char *poldiff_role_trans_get_original_default(const poldiff_role_trans_t * role_trans);
+
+/**
+ * Get the modified default type from a role_transition diff. Note that if
+ * this rule was removed (form POLDIFF_FORM_REMOVED or
+ * POLDIFF_FORM_REMOVE_TYPE) then the return value will be NULL.
+ *
+ * @param role_trans The role_transition rule from which to get the
+ * modified default role.
+ *
+ * @return Name of the modified default role. If there was no modified role or
+ * upon error then return NULL. The caller should not free the returned
+ * string.
+ */
+ extern const char *poldiff_role_trans_get_modified_default(const poldiff_role_trans_t * role_trans);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_RBAC_DIFF_H */
diff --git a/libpoldiff/include/poldiff/role_diff.h b/libpoldiff/include/poldiff/role_diff.h
new file mode 100644
index 0000000..9526cb5
--- /dev/null
+++ b/libpoldiff/include/poldiff/role_diff.h
@@ -0,0 +1,127 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in roles.
+ *
+ * @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 POLDIFF_ROLE_DIFF_H
+#define POLDIFF_ROLE_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_role poldiff_role_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for roles.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_role_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of role differences from the role difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * role difference summary.
+ *
+ * @return A vector of elements of type poldiff_role_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_role_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a role.
+ *
+ * @param diff The policy difference structure associated with the role.
+ * @param role The role from which to generate the string.
+ *
+ * @return A string representation of role difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_role_to_string(const poldiff_t * diff, const void *role);
+
+/**
+ * Get the name of the role from a role diff.
+ *
+ * @param role The role from which to get the name.
+ *
+ * @return Name of the role on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_role_get_name(const poldiff_role_t * role);
+
+/**
+ * Get the form of difference from a role diff.
+ *
+ * @param role The role from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_role_get_form(const void *role);
+
+/**
+ * Get a vector of types added to the role.
+ *
+ * @param role The role diff from which to get the types vector.
+ *
+ * @return a vector of type names (type char *) that are allowed to
+ * the role in the modified policy. If no types were added the size
+ * of the returned vector will be 0. The caller must not destroy
+ * this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_role_get_added_types(const poldiff_role_t * role);
+
+/**
+ * Get a vector of types removed from the role.
+ *
+ * @param role The role diff from which to get the types vector.
+ *
+ * @return A vector of type names (type char *) that are allowed to
+ * the role in the original policy. If no types were removed the
+ * size of the returned vector will be 0. The caller must not
+ * destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_role_get_removed_types(const poldiff_role_t * role);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_ROLE_DIFF_H */
diff --git a/libpoldiff/include/poldiff/terule_diff.h b/libpoldiff/include/poldiff/terule_diff.h
new file mode 100644
index 0000000..6d09e9d
--- /dev/null
+++ b/libpoldiff/include/poldiff/terule_diff.h
@@ -0,0 +1,262 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in te rules
+ * (type_transition, type_change, type_member).
+ *
+ * @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 POLDIFF_TERULE_DIFF_H
+#define POLDIFF_TERULE_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_terule poldiff_terule_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for TE type_change rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_terule_get_stats_change(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for TE type_member rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_terule_get_stats_member(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for TE type_transition rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_terule_get_stats_trans(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of te rule differences from the te rule difference
+ * summary for just type_change rules.
+ *
+ * @param diff The policy difference structure associated with the te
+ * rule difference summary.
+ *
+ * @return A vector of elements of type poldiff_terule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_terule_vector_change(const poldiff_t * diff);
+
+/**
+ * Get the vector of te rule differences from the te rule difference
+ * summary for just type_member rules.
+ *
+ * @param diff The policy difference structure associated with the te
+ * rule difference summary.
+ *
+ * @return A vector of elements of type poldiff_terule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_terule_vector_member(const poldiff_t * diff);
+
+/**
+ * Get the vector of te rule differences from the te rule difference
+ * summary for just type_transition rules.
+ *
+ * @param diff The policy difference structure associated with the te
+ * rule difference summary.
+ *
+ * @return A vector of elements of type poldiff_terule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_terule_vector_trans(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a te rule.
+ *
+ * @param diff The policy difference structure associated with the te
+ * rule.
+ * @param terule The te rule from which to generate the string.
+ *
+ * @return A string representation of te rule difference; the caller
+ * is responsible for free()ing this string. On error, return NULL
+ * and set errno.
+ */
+ extern char *poldiff_terule_to_string(const poldiff_t * diff, const void *terule);
+
+/**
+ * Get the form of difference from a te rule diff.
+ *
+ * @param terule The te rule from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error.
+ */
+ extern poldiff_form_e poldiff_terule_get_form(const void *terule);
+
+/**
+ * Get the type of rule this is from a te rule diff.
+ *
+ * @param avrule The av rule from which to get the rule type.
+ *
+ * @return One of QPOL_RULE_TYPE_TRANS etc, suitable for printing via
+ * apol_rule_type_to_str().
+ */
+ extern uint32_t poldiff_terule_get_rule_type(const poldiff_terule_t * terule);
+
+/**
+ * Get the source type from a te rule diff.
+ *
+ * @param terule The te rule from which to get the type.
+ *
+ * @return A string for the type. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_terule_get_source_type(const poldiff_terule_t * terule);
+
+/**
+ * Get the target type from a te rule diff.
+ *
+ * @param terule The te rule from which to get the type.
+ *
+ * @return A string for the type. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_terule_get_target_type(const poldiff_terule_t * terule);
+
+/**
+ * Get the object class from a te rule diff.
+ *
+ * @param terule The te rule from which to get the class.
+ *
+ * @return A string for the class. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_terule_get_object_class(const poldiff_terule_t * terule);
+
+/**
+ * Get the conditional expression from a te rule diff. Note that
+ * this really returns a qpol_cond_t and an apol_policy_t, which may
+ * then be used in other routines such as apol_cond_expr_render().
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param terule The te rule from which to get the conditional.
+ * @param cond Reference to the rule's conditional pointer, or NULL
+ * if the rule is not conditional. The caller must not free() this
+ * pointer.
+ * @param which_list Reference to which list the rule belongs, either
+ * 1 if in the true branch, 0 if in false. If the rule is not
+ * conditional then this value will be set to 1.
+ * @param p Reference to the policy from which the conditional
+ * originated, or NULL if the rule is not conditional. The caller
+ * must not destroy this pointer.
+ */
+ extern void poldiff_terule_get_cond(const poldiff_t * diff, const poldiff_terule_t * terule,
+ const qpol_cond_t ** cond, uint32_t * which_list, const apol_policy_t ** p);
+
+/**
+ * Get the original default type for this type rule. Note that if
+ * this rule was added (form POLDIFF_FORM_ADDED or
+ * POLDIFF_FORM_ADD_TYPE) then the return value will be NULL.
+ *
+ * @param terule The te rule diff from which to get the original
+ * default type.
+ *
+ * @return Original default type. If there was no original type or
+ * upon error then return NULL. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_terule_get_original_default(const poldiff_terule_t * terule);
+
+/**
+ * Get the modified default type for this type rule. Note that if
+ * this rule was removed (form POLDIFF_FORM_REMOVED or
+ * POLDIFF_FORM_REMOVE_TYPE) then the return value will be NULL.
+ *
+ * @param terule The te rule diff from which to get the modified
+ * default type.
+ *
+ * @return Modified default type. If there was no modified type or
+ * upon error then return NULL. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_terule_get_modified_default(const poldiff_terule_t * terule);
+
+/**
+ * Get a vector of line numbers (of type unsigned long) for this te rule
+ * difference from the original policy. Note that if the form is
+ * POLDIFF_FORM_ADDED or POLDIFF_FORM_ADD_TYPE then this will return NULL.
+ * Also, if the original policy is a binary policy or line numbers are not yet
+ * enabled then this returns NULL.
+ * @see poldiff_enable_line_numbers() to enable line numbers.
+ *
+ * @param terule The te rule diff from which to get line numbers.
+ *
+ * @return A vector of line numbers (type unsigned long) for the rule
+ * in the original policy, or NULL if no numbers are available.
+ */
+ extern apol_vector_t *poldiff_terule_get_orig_line_numbers(const poldiff_terule_t * terule);
+
+/**
+ * Get a vector of line numbers (of type unsigned long) for this te rule
+ * difference from the modified policy. Note that if the form is
+ * POLDIFF_FORM_REMOVED or POLDIFF_FORM_REMOVE_TYPE then this will return
+ * NULL. Also, if the modified policy is a binary policy or line numbers are
+ * not yet enabled then this returns NULL.
+ * @see poldiff_enable_line_numbers() to enable line numbers.
+ *
+ * @param terule The te rule diff from which to get line numbers.
+ *
+ * @return A vector of line numbers (type unsigned long) for the rule
+ * in the modified policy, or NULL if no numbers are available.
+ */
+ extern apol_vector_t *poldiff_terule_get_mod_line_numbers(const poldiff_terule_t * terule);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_TERULE_DIFF_H */
diff --git a/libpoldiff/include/poldiff/type_diff.h b/libpoldiff/include/poldiff/type_diff.h
new file mode 100644
index 0000000..b92795b
--- /dev/null
+++ b/libpoldiff/include/poldiff/type_diff.h
@@ -0,0 +1,132 @@
+/**
+ * @file
+ * Public interface for computing semantic differences of primary
+ * types. Aliases are resolved by the type mapping system
+ * (type_map.h); attributes are found in the attrib_diff.h 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 POLDIFF_TYPE_DIFF_H
+#define POLDIFF_TYPE_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_type poldiff_type_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for types.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_type_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of type differences from the type difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * type difference summary.
+ *
+ * @return A vector of elements of type poldiff_type_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_type_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a type.
+ *
+ * @param diff The policy difference structure associated with the type.
+ * @param type The type from which to generate the string.
+ *
+ * @return A string representation of type difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_type_to_string(const poldiff_t * diff, const void *type);
+
+/**
+ * Get the name of the type from a type diff.
+ *
+ * @param type The type from which to get the name.
+ *
+ * @return Name of the type on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_type_get_name(const poldiff_type_t * type);
+
+/**
+ * Get the form of difference from a type diff.
+ *
+ * @param cls The type from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_type_get_form(const void *type);
+
+/**
+ * Get a vector of attributes added to the type.
+ *
+ * @param type The type diff from which to get the attribute
+ * vector.
+ *
+ * @return A vector of attribute names (type char *) that are
+ * assigned to the type in the modified policy. If no attributes
+ * were added the size of the returned vector will be 0. The
+ * caller must not destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_type_get_added_attribs(const poldiff_type_t * type);
+
+/**
+ * Get a vector of attributes removed from the type.
+ *
+ * @param type The type diff from which to get the attribute
+ * vector.
+ *
+ * @return A vector of attribute names (type char *) that are
+ * assigned to the type in the original policy. If no attributes
+ * were removed the size of the returned vector will be 0. The
+ * caller must not destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_type_get_removed_attribs(const poldiff_type_t * type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_TYPE_DIFF_H */
diff --git a/libpoldiff/include/poldiff/type_map.h b/libpoldiff/include/poldiff/type_map.h
new file mode 100644
index 0000000..56eb742
--- /dev/null
+++ b/libpoldiff/include/poldiff/type_map.h
@@ -0,0 +1,153 @@
+/**
+ * @file
+ * Public interface for type equivalence mapping for semantic
+ * difference calculations.
+ *
+ * @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 POLDIFF_TYPE_MAP_H
+#define POLDIFF_TYPE_MAP_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <poldiff/poldiff.h>
+#include <apol/vector.h>
+
+ typedef struct poldiff_type_remap_entry poldiff_type_remap_entry_t;
+
+/**
+ * Note that a type(s) from the original policy should be remapped in
+ * the modified policy. Subsequent diffs will treat type(s) in
+ * orig_names to be equivalent to type(s) in mod_names. The created
+ * remap entry will be marked as enabled.
+ *
+ * It is an error for the size of both vectors to be greater than
+ * one.
+ *
+ * Note that you may only remap primary types, not attributes nor
+ * aliases.
+ *
+ * @param diff The difference structure associated with the types.
+ * Note that renaming a type will reset the status of previously run
+ * difference calculations and they will need to be rerun.
+ * @param orig_names A vector of type names (char *) in the original
+ * policy.
+ * @param mod_name A vector of type names (char *) in the modified
+ * policy to consider equivalent.
+ *
+ * @return 0 on success or < 0 on error; if the call fails, errno
+ * will be set and the poldiff object remains unchanged.
+ */
+ extern int poldiff_type_remap_create(poldiff_t * diff, const apol_vector_t * orig_names, const apol_vector_t * mod_names);
+
+/**
+ * Get a vector of all identified type remap entries. The caller may
+ * then manipulate this list by selectively enabling/disabling
+ * individual entries.
+ *
+ * @param diff The difference structure associated with the types
+ * remaps.
+ *
+ * @return Vector of poldiff_type_remap_entry_t objects. The caller
+ * should not destroy this vector.
+ */
+ extern apol_vector_t *poldiff_type_remap_get_entries(const poldiff_t * diff);
+
+/**
+ * Remove a poldiff_type_remap_entry object. This function will
+ * destroy the entry object afterwards.
+ *
+ * @param diff The difference structure associated with the types
+ * remaps.
+ * @param entry Type remap entry to remove and destroy.
+ */
+ extern void poldiff_type_remap_entry_remove(poldiff_t * diff, poldiff_type_remap_entry_t * entry);
+
+/**
+ * Allocate and return a sorted vector of type names (char *)
+ * corresponding to the original types within a
+ * poldiff_type_remap_entry_t object. The strings themselves are to
+ * be considered immutable; if the caller needs them for future use
+ * it should duplicate them.
+ *
+ * @param diff Difference structure, for error reporting.
+ * @param entry Remap entry from which to get type names.
+ *
+ * @return Vector of type names. The caller is responsible for
+ * calling apol_vector_destroy() afterwards. Upon error return NULL
+ * and set errno.
+ */
+ extern apol_vector_t *poldiff_type_remap_entry_get_original_types(const poldiff_t * diff,
+ const poldiff_type_remap_entry_t * entry);
+
+/**
+ * Allocate and return a sorted vector of type names (char *)
+ * corresponding to the modified types within a
+ * poldiff_type_remap_entry_t object. The strings themselves are to
+ * be considered immutable; if the caller needs them for future use
+ * it should duplicate them.
+ *
+ * @param diff Difference structure, for error reporting.
+ * @param entry Remap entry from which to get type names.
+ *
+ * @return Vector of type names. The caller is responsible for
+ * calling apol_vector_destroy() afterwards. Upon error return NULL
+ * and set errno.
+ */
+ extern apol_vector_t *poldiff_type_remap_entry_get_modified_types(const poldiff_t * diff,
+ const poldiff_type_remap_entry_t * entry);
+
+/**
+ * Given a poldiff_type_remap_entry_t object, determine if was
+ * an inferred mapping or not.
+ *
+ * @param entry Remap entry from which to get its inference status.
+ *
+ * @return 1 if it was inferred, 0 if not, < 0 on error.
+ */
+ extern int poldiff_type_remap_entry_get_is_inferred(const poldiff_type_remap_entry_t * entry);
+
+/**
+ * Given a poldiff_type_remap_entry_t object, determine if it is
+ * enabled or not.
+ *
+ * @param entry Remap entry from which to get its enabled status.
+ *
+ * @return 1 if it is enabled, 0 if not, < 0 on error.
+ */
+ extern int poldiff_type_remap_entry_get_is_enabled(const poldiff_type_remap_entry_t * entry);
+
+/**
+ * Enable or disable a poldiff_type_remap_entry_t object.
+ *
+ * @param entry Remap entry from which to set its enabled status.
+ * @param enabled 0 to disable this entry, non-zero to enable it.
+ */
+ extern void poldiff_type_remap_entry_set_enabled(poldiff_type_remap_entry_t * entry, int enabled);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_TYPE_MAP_H */
diff --git a/libpoldiff/include/poldiff/user_diff.h b/libpoldiff/include/poldiff/user_diff.h
new file mode 100644
index 0000000..0af50e6
--- /dev/null
+++ b/libpoldiff/include/poldiff/user_diff.h
@@ -0,0 +1,191 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in users.
+ *
+ * @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 POLDIFF_USER_DIFF_H
+#define POLDIFF_USER_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_user poldiff_user_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for users.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_user_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of user differences from the user difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * user difference summary.
+ *
+ * @return A vector of elements of type poldiff_user_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_user_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a user.
+ *
+ * @param diff The policy difference structure associated with the user.
+ * @param user The user from which to generate the string.
+ *
+ * @return A string representation of user difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_user_to_string(const poldiff_t * diff, const void *user);
+
+/**
+ * Get the name of the user from a user diff.
+ *
+ * @param user The user from which to get the name.
+ *
+ * @return Name of the user on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_user_get_name(const poldiff_user_t * user);
+
+/**
+ * Get the form of difference from a user diff.
+ *
+ * @param user The user from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_user_get_form(const void *user);
+
+/**
+ * Get a vector of unmodified roles for the user.
+ *
+ * @param user The user diff from which to get the roles vector.
+ *
+ * @return A vector of role names (type char *) that are assigned to
+ * the user in the modified policy. If no roles were added the size
+ * of the returned vector will be 0. The caller must not destroy
+ * this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_user_get_unmodified_roles(const poldiff_user_t * user);
+
+/**
+ * Get a vector of roles added to the user. If a user was added by
+ * the modified policy then this vector will hold all of the roles.
+ *
+ * @param user The user diff from which to get the roles vector.
+ *
+ * @return A vector of role names (type char *) that are assigned to
+ * the user in the modified policy. If no roles were added the size
+ * of the returned vector will be 0. The caller must not destroy
+ * this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_user_get_added_roles(const poldiff_user_t * user);
+
+/**
+ * Get a vector of roles removed from the user. If a user was
+ * removed by the modified policy then this vector will hold all of
+ * the roles.
+ *
+ * @param user The user diff from which to get the roles vector.
+ *
+ * @return A vector of role names (type char *) that are assigned to
+ * the user in the original policy. If no roles were removed the
+ * size of the returned vector will be 0. The caller must not
+ * destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_user_get_removed_roles(const poldiff_user_t * user);
+
+/**
+ * Get the original user's default MLS level. That is, this is the
+ * level assigned to the user in the original policy. If the level
+ * has the form POLDIFF_FORM_MODIFIED, then this indiciates that the
+ * user had the same sensitivity between the two policies but
+ * different categories.
+ *
+ * If neither policy is MLS or there are no differences in default
+ * level, then the return value is NULL.
+ *
+ * @param user The user diff from which to get default level.
+ *
+ * @return User's original default MLS level. Returns NULL upon
+ * error or if there is no difference in level.
+ */
+ extern const poldiff_level_t *poldiff_user_get_original_dfltlevel(const poldiff_user_t * user);
+
+/**
+ * Get the modified user's MLS level. That is, this is the level
+ * assigned to the user in the modified policy. If the level had the
+ * same sensitivity but different categories call
+ * poldiff_user_get_original_dfltlevel() to get the difference; this
+ * function will return NULL.
+ *
+ * If neither policy is MLS or there are no differences in
+ * default level, then the return value is NULL.
+ *
+ * @param user The user diff from which to get default level.
+ *
+ * @return User's modified default MLS level. Returns NULL upon
+ * error, if there is no difference in level, or if the sensitivity
+ * was unchanged.
+ */
+ extern const poldiff_level_t *poldiff_user_get_modified_dfltlevel(const poldiff_user_t * user);
+
+/**
+ * Get the change in user's assigned MLS range.
+ *
+ * If neither policy is MLS or there are no differences in range,
+ * then the return value is NULL.
+ *
+ * @param user The user diff from which to get assigned range
+ * differences.
+ *
+ * @return User's MLS range differences. Returns NULL upon error or
+ * if there is no difference in range.
+ */
+ extern const poldiff_range_t *poldiff_user_get_range(const poldiff_user_t * user);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_USER_DIFF_H */
diff --git a/libpoldiff/include/poldiff/util.h b/libpoldiff/include/poldiff/util.h
new file mode 100644
index 0000000..445a92b
--- /dev/null
+++ b/libpoldiff/include/poldiff/util.h
@@ -0,0 +1,45 @@
+/**
+ * @file
+ *
+ * Miscellaneous, uncategorized functions for libpoldiff.
+ *
+ * @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 POLDIFF_UTIL_H
+#define POLDIFF_UTIL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * Return an immutable string describing this library's version.
+ *
+ * @return String describing this library.
+ */
+ extern const char *libpoldiff_get_version(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libpoldiff/src/Makefile.am b/libpoldiff/src/Makefile.am
new file mode 100644
index 0000000..8275309
--- /dev/null
+++ b/libpoldiff/src/Makefile.am
@@ -0,0 +1,57 @@
+lib_LIBRARIES = libpoldiff.a
+
+poldiffso_DATA = libpoldiff.so.@libpoldiff_version@
+poldiffsodir = $(libdir)
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ \
+ @APOL_CFLAGS@ @QPOL_CFLAGS@ -I$(srcdir)/../include -fpic
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+libpoldiff_a_SOURCES = \
+ poldiff.c \
+ attrib_diff.c attrib_internal.h \
+ avrule_diff.c avrule_internal.h \
+ bool_diff.c bool_internal.h \
+ cat_diff.c cat_internal.h \
+ class_diff.c class_internal.h \
+ level_diff.c level_internal.h \
+ range_diff.c range_internal.h \
+ range_trans_diff.c range_trans_internal.h \
+ rbac_diff.c rbac_internal.h \
+ role_diff.c role_internal.h \
+ terule_diff.c terule_internal.h \
+ type_diff.c type_internal.h \
+ user_diff.c user_internal.h \
+ poldiff_internal.h \
+ type_map.c type_map_internal.h \
+ util.c
+
+libpoldiff_a_DEPENDENCIES = $(top_builddir)/libapol/src/libapol.so
+
+libpoldiff_so_OBJS = $(patsubst %.c,%.o,$(filter %.c,$(libpoldiff_a_SOURCES)))
+LIBPOLDIFF_SONAME = @libpoldiff_soname@
+
+dist_noinst_DATA = libpoldiff.map writing-diffs-HOWTO
+
+$(poldiffso_DATA): $(libpoldiff_so_OBJS) libpoldiff.map
+ $(CC) -shared -o $@ $(libpoldiff_so_OBJS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(LIBPOLDIFF_SONAME),--version-script=$(srcdir)/libpoldiff.map,-z,defs $(top_builddir)/libqpol/src/libqpol.so $(top_builddir)/libapol/src/libapol.so
+ $(LN_S) -f $@ @libpoldiff_soname@
+ $(LN_S) -f $@ libpoldiff.so
+
+libpoldiff.so: $(poldiffso_DATA)
+
+$(top_builddir)/libapol/src/libapol.so:
+ $(MAKE) -C $(top_builddir)/libapol/src $(notdir $@)
+
+$(top_builddir)/libqpol/src/libqpol.so:
+ $(MAKE) -C $(top_builddir)/libqpol/src $(notdir $@)
+
+install-data-hook:
+ cd $(DESTDIR)$(poldiffsodir) && $(LN_S) -f $(poldiffso_DATA) @libpoldiff_soname@
+ cd $(DESTDIR)$(poldiffsodir) && $(LN_S) -f $(poldiffso_DATA) libpoldiff.so
+
+mostlyclean-local:
+ -rm -rf *.gcno *.gcda *.gprof *.gcov libpoldiff.so @libpoldiff_soname@ $(poldiffso_DATA)
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(poldiffsodir)/$(poldiffso_DATA) $(DESTDIR)$(poldiffsodir)/@libpoldiff_soname@ $(DESTDIR)$(poldiffsodir)/libpoldiff.so
diff --git a/libpoldiff/src/attrib_diff.c b/libpoldiff/src/attrib_diff.c
new file mode 100644
index 0000000..a9802df
--- /dev/null
+++ b/libpoldiff/src/attrib_diff.c
@@ -0,0 +1,544 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in attributes.
+ *
+ * @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 "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_attrib_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_attrib
+{
+ char *name;
+ poldiff_form_e form;
+ apol_vector_t *added_types;
+ apol_vector_t *removed_types;
+};
+
+void poldiff_attrib_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->attrib_diffs->num_added;
+ stats[1] = diff->attrib_diffs->num_removed;
+ stats[2] = diff->attrib_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+char *poldiff_attrib_to_string(const poldiff_t * diff, const void *attrib)
+{
+ poldiff_attrib_t *at = (poldiff_attrib_t *) attrib;
+ size_t num_added, num_removed, len = 0, i;
+ char *s = NULL, *type;
+ if (diff == NULL || attrib == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ num_added = apol_vector_get_size(at->added_types);
+ num_removed = apol_vector_get_size(at->removed_types);
+ switch (at->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", at->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", at->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "* %s (", at->name) < 0) {
+ break;
+ }
+ if (num_added > 0 && apol_str_appendf(&s, &len, "%zd Added Type%s", num_added, (num_added == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ if (num_removed > 0
+ && apol_str_appendf(&s, &len, "%s%zd Removed Type%s", (num_added > 0 ? ", " : ""), num_removed,
+ (num_removed == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ if (apol_str_append(&s, &len, ")\n") < 0) {
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(at->added_types); i++) {
+ type = (char *)apol_vector_get_element(at->added_types, i);
+ if (apol_str_appendf(&s, &len, "\t+ %s\n", type) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(at->removed_types); i++) {
+ type = (char *)apol_vector_get_element(at->removed_types, i);
+ if (apol_str_appendf(&s, &len, "\t- %s\n", type) < 0) {
+ goto err;
+ }
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ err:
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_attrib_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->attrib_diffs->diffs;
+}
+
+const char *poldiff_attrib_get_name(const poldiff_attrib_t * attrib)
+{
+ if (attrib == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return attrib->name;
+}
+
+poldiff_form_e poldiff_attrib_get_form(const void *attrib)
+{
+ if (attrib == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_attrib_t *)attrib)->form;
+}
+
+const apol_vector_t *poldiff_attrib_get_added_types(const poldiff_attrib_t * attrib)
+{
+ if (attrib == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return attrib->added_types;
+}
+
+const apol_vector_t *poldiff_attrib_get_removed_types(const poldiff_attrib_t * attrib)
+{
+ if (attrib == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return attrib->removed_types;
+}
+
+/*************** protected functions for attribs ***************/
+
+static void attrib_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_attrib_t *t = (poldiff_attrib_t *) elem;
+ free(t->name);
+ apol_vector_destroy(&t->added_types);
+ apol_vector_destroy(&t->removed_types);
+ free(t);
+ }
+}
+
+poldiff_attrib_summary_t *attrib_summary_create(void)
+{
+ poldiff_attrib_summary_t *rs = calloc(1, sizeof(*rs));
+ if (rs == NULL) {
+ return NULL;
+ }
+ if ((rs->diffs = apol_vector_create(attrib_free)) == NULL) {
+ attrib_summary_destroy(&rs);
+ return NULL;
+ }
+ return rs;
+}
+
+void attrib_summary_destroy(poldiff_attrib_summary_t ** rs)
+{
+ if (rs != NULL && *rs != NULL) {
+ apol_vector_destroy(&(*rs)->diffs);
+ free(*rs);
+ *rs = NULL;
+ }
+}
+
+int attrib_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ attrib_summary_destroy(&diff->attrib_diffs);
+ diff->attrib_diffs = attrib_summary_create();
+ if (diff->attrib_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two attribs from the same policy.
+ */
+static int attrib_name_comp(const void *x, const void *y, void *arg)
+{
+ const qpol_type_t *r1 = x;
+ const qpol_type_t *r2 = y;
+ apol_policy_t *p = (apol_policy_t *) arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+ if (qpol_type_get_name(q, r1, &name1) < 0 || qpol_type_get_name(q, r2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *attrib_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_type_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create(NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ unsigned char isattr;
+ qpol_type_t *type;
+ qpol_iterator_get_item(iter, (void **)&type);
+ qpol_type_get_isattr(q, type, &isattr);
+ if (isattr) {
+ apol_vector_append(v, type);
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, attrib_name_comp, (void *)policy);
+ return v;
+}
+
+int attrib_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ const qpol_type_t *r1 = x;
+ const qpol_type_t *r2 = y;
+ const char *name1, *name2;
+ if (qpol_type_get_name(diff->orig_qpol, r1, &name1) < 0 || qpol_type_get_name(diff->mod_qpol, r2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new attrib difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the attrib that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling attrib_free() upon the returned
+ * value.
+ */
+static poldiff_attrib_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_attrib_t *pr;
+ int error;
+ if ((pr = calloc(1, sizeof(*pr))) == NULL ||
+ (pr->name = strdup(name)) == NULL ||
+ (pr->added_types = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pr->removed_types = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = errno;
+ attrib_free(pr);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pr->form = form;
+ return pr;
+}
+
+int attrib_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const qpol_type_t *r = item;
+ const char *name = NULL;
+ poldiff_attrib_t *pr;
+ int error;
+ if ((form == POLDIFF_FORM_ADDED &&
+ qpol_type_get_name(diff->mod_qpol, r, &name) < 0) ||
+ ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_type_get_name(diff->orig_qpol, r, &name) < 0))
+ {
+ return -1;
+ }
+ pr = make_diff(diff, form, name);
+ if (pr == NULL) {
+ return -1;
+ }
+ if (apol_vector_append(diff->attrib_diffs->diffs, pr) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ attrib_free(pr);
+ errno = error;
+ return -1;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->attrib_diffs->num_added++;
+ } else {
+ diff->attrib_diffs->num_removed++;
+ }
+ return 0;
+}
+
+/**
+ * Given a attrib, return an unsorted vector of its allowed types (in
+ * the form of uint32_t corresponding to pseudo-type values).
+ *
+ * @param diff Policy diff error handler.
+ * @param attrib Attrib whose types to get.
+ * @param which Which policy, one of POLDIFF_POLICY_ORIG or
+ * POLDIFF_POLICY_MOD.
+ *
+ * @return Vector of pseudo-type values. The caller is responsible
+ * for calling apol_vector_destroy(). On error, return NULL.
+ */
+static apol_vector_t *attrib_get_types(const poldiff_t * diff, const qpol_type_t * attrib, int which)
+{
+ qpol_iterator_t *iter = NULL;
+ const qpol_type_t *type;
+ uint32_t new_val;
+ apol_vector_t *v = NULL;
+ int retval = -1, error = 0;
+
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (which == POLDIFF_POLICY_ORIG) {
+ if (qpol_type_get_type_iter(diff->orig_qpol, attrib, &iter) < 0) {
+ goto cleanup;
+ }
+ } else {
+ if (qpol_type_get_type_iter(diff->mod_qpol, attrib, &iter) < 0) {
+ goto cleanup;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0 || (new_val = type_map_lookup(diff, type, which)) == 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_append(v, (void *)((size_t) new_val)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+int attrib_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const qpol_type_t *r1 = x;
+ const qpol_type_t *r2 = y;
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ apol_vector_t *added_types = NULL, *removed_types = NULL;
+ const apol_vector_t *reverse_v;
+ const char *name;
+ char *new_name;
+ uint32_t t1, t2;
+ poldiff_attrib_t *r = NULL;
+ qpol_type_t *t;
+ size_t i, j;
+ int retval = -1, error = 0;
+ if (qpol_type_get_name(diff->orig_qpol, r1, &name) < 0 ||
+ (v1 = attrib_get_types(diff, r1, POLDIFF_POLICY_ORIG)) == NULL ||
+ (v2 = attrib_get_types(diff, r2, POLDIFF_POLICY_MOD)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_sort_uniquify(v1, NULL, NULL);
+ apol_vector_sort_uniquify(v2, NULL, NULL);
+ if ((added_types = apol_vector_create(NULL)) == NULL || (removed_types = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2))
+ break;
+ t1 = (uint32_t) ((size_t) apol_vector_get_element(v1, i));
+ t2 = (uint32_t) ((size_t) apol_vector_get_element(v2, j));
+ if (t2 > t1) {
+ if (apol_vector_append(removed_types, (void *)((size_t) t1)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (t1 > t2) {
+ if (apol_vector_append(added_types, (void *)((size_t) t2)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ t1 = (uint32_t) ((size_t) apol_vector_get_element(v1, i));
+ if (apol_vector_append(removed_types, (void *)((size_t) t1)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ t2 = (uint32_t) ((size_t) apol_vector_get_element(v2, j));
+ if (apol_vector_append(added_types, (void *)((size_t) t2)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_get_size(added_types) > 0 || apol_vector_get_size(removed_types) > 0) {
+ if ((r = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(removed_types); i++) {
+ t1 = (uint32_t) ((size_t) apol_vector_get_element(removed_types, i));
+ if ((reverse_v = type_map_lookup_reverse(diff, t1, POLDIFF_POLICY_ORIG)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (j = 0; j < apol_vector_get_size(reverse_v); j++) {
+ t = (qpol_type_t *) apol_vector_get_element(reverse_v, j);
+ if (qpol_type_get_name(diff->orig_qpol, t, &name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((new_name = strdup(name)) == NULL || apol_vector_append(r->removed_types, new_name) < 0) {
+ error = errno;
+ free(new_name);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(added_types); i++) {
+ t2 = (uint32_t) ((size_t) apol_vector_get_element(added_types, i));
+ if ((reverse_v = type_map_lookup_reverse(diff, t2, POLDIFF_POLICY_MOD)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (j = 0; j < apol_vector_get_size(reverse_v); j++) {
+ t = (qpol_type_t *) apol_vector_get_element(reverse_v, j);
+ if (qpol_type_get_name(diff->mod_qpol, t, &name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((new_name = strdup(name)) == NULL || apol_vector_append(r->added_types, new_name) < 0) {
+ error = errno;
+ free(new_name);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ }
+ apol_vector_sort(r->removed_types, apol_str_strcmp, NULL);
+ apol_vector_sort(r->added_types, apol_str_strcmp, NULL);
+ if (apol_vector_append(diff->attrib_diffs->diffs, r) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->attrib_diffs->num_modified++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ apol_vector_destroy(&added_types);
+ apol_vector_destroy(&removed_types);
+ if (retval != 0) {
+ attrib_free(r);
+ }
+ errno = error;
+ return retval;
+}
diff --git a/libpoldiff/src/attrib_internal.h b/libpoldiff/src/attrib_internal.h
new file mode 100644
index 0000000..75d1e1d
--- /dev/null
+++ b/libpoldiff/src/attrib_internal.h
@@ -0,0 +1,121 @@
+/**
+ * @file
+ * Protected interface for attribute differences.
+ *
+ * @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 POLDIFF_ATTRIB_INTERNAL_H
+#define POLDIFF_ATTRIB_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_attrib_summary poldiff_attrib_summary_t;
+
+/**
+ * Allocate and return a new poldiff_attrib_summary_t object.
+ *
+ * @return A new attrib summary. The caller must call attrib_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_attrib_summary_t *attrib_summary_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_attrib_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param us Reference to a attrib summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void attrib_summary_destroy(poldiff_attrib_summary_t ** us);
+
+/**
+ * Reset the state of all attribute differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int attrib_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all attribs from the given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of all attribs. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *attrib_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_attrib_t objects, determining if they have the same
+ * name or not.
+ *
+ * @param x The attrib from the original policy.
+ * @param y The attrib from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if attrib x is respectively less than, equal
+ * to, or greater than attrib y.
+ */
+ int attrib_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a attrib.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int attrib_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two attribs for which the compare
+ * callback returns 0. If a difference is found then allocate,
+ * initialize, and insert a new semantic difference entry for that
+ * attrib.
+ *
+ * @param diff The policy difference structure associated with both
+ * attribs and to which to add an entry if needed.
+ * @param x The attrib from the original policy.
+ * @param y The attrib from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int attrib_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_ATTRIB_INTERNAL_H */
diff --git a/libpoldiff/src/avrule_diff.c b/libpoldiff/src/avrule_diff.c
new file mode 100644
index 0000000..a1b61b1
--- /dev/null
+++ b/libpoldiff/src/avrule_diff.c
@@ -0,0 +1,1636 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in AV and Type
+ * 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
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/policy-query.h>
+#include <apol/util.h>
+#include <qpol/policy_extend.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_avrule_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ size_t num_added_type;
+ size_t num_removed_type;
+ int diffs_sorted;
+ /** vector of poldiff_avrule_t */
+ apol_vector_t *diffs;
+};
+
+struct poldiff_avrule
+{
+ uint32_t spec;
+ /* pointer into policy's symbol table */
+ const char *source, *target;
+ /** the class string is pointer into the class_bst BST */
+ char *cls;
+ poldiff_form_e form;
+ /** vector of pointers into the perm_bst BST (char *) */
+ apol_vector_t *unmodified_perms;
+ /** vector of pointers into the perm_bst BST (char *) */
+ apol_vector_t *added_perms;
+ /** vector of pointers into the perm_bst BST (char *) */
+ apol_vector_t *removed_perms;
+ /** pointer into policy's conditional list, needed to render
+ * conditional expressions */
+ const qpol_cond_t *cond;
+ uint32_t branch;
+ /** vector of unsigned longs of line numbers from original policy */
+ apol_vector_t *orig_linenos;
+ /** vector of unsigned longs of line numbers from modified policy */
+ apol_vector_t *mod_linenos;
+ /** array of pointers for original rules */
+ qpol_avrule_t **orig_rules;
+ size_t num_orig_rules;
+ /** array of pointers for modified rules */
+ qpol_avrule_t **mod_rules;
+ size_t num_mod_rules;
+};
+
+typedef struct pseudo_avrule
+{
+ uint32_t spec;
+ /** pseudo-type values */
+ uint32_t source, target;
+ /** pointer into the class_bst BST */
+ char *cls;
+ /** array of pointers into the perm_bst BST */
+ /* (use an array here to save space) */
+ char **perms;
+ size_t num_perms;
+ /** array of pointers into the bool_bst BST */
+ char *bools[5];
+ uint32_t bool_val;
+ uint32_t branch;
+ /** pointer into policy's conditional list, needed to render
+ * conditional expressions */
+ const qpol_cond_t *cond;
+ /** array of qpol_avrule_t pointers, for showing line numbers */
+ const qpol_avrule_t **rules;
+ size_t num_rules;
+} pseudo_avrule_t;
+
+/******************** public avrule functions ********************/
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for av rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ * @param idx Index into the avrule differences specifying which
+ * avrule type to get, one of AVRULE_OFFSET_ALLOW, etc.
+ */
+static void poldiff_avrule_get_stats(const poldiff_t * diff, size_t stats[5], avrule_offset_e idx)
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->avrule_diffs[idx]->num_added;
+ stats[1] = diff->avrule_diffs[idx]->num_removed;
+ stats[2] = diff->avrule_diffs[idx]->num_modified;
+ stats[3] = diff->avrule_diffs[idx]->num_added_type;
+ stats[4] = diff->avrule_diffs[idx]->num_removed_type;
+}
+
+void poldiff_avrule_get_stats_allow(const poldiff_t * diff, size_t stats[5])
+{
+ poldiff_avrule_get_stats(diff, stats, AVRULE_OFFSET_ALLOW);
+}
+
+void poldiff_avrule_get_stats_neverallow(const poldiff_t * diff, size_t stats[5])
+{
+ poldiff_avrule_get_stats(diff, stats, AVRULE_OFFSET_NEVERALLOW);
+}
+
+void poldiff_avrule_get_stats_dontaudit(const poldiff_t * diff, size_t stats[5])
+{
+ poldiff_avrule_get_stats(diff, stats, AVRULE_OFFSET_DONTAUDIT);
+}
+
+void poldiff_avrule_get_stats_auditallow(const poldiff_t * diff, size_t stats[5])
+{
+ poldiff_avrule_get_stats(diff, stats, AVRULE_OFFSET_AUDITALLOW);
+}
+
+char *poldiff_avrule_to_string(const poldiff_t * diff, const void *avrule)
+{
+ const poldiff_avrule_t *pa = (const poldiff_avrule_t *)avrule;
+ apol_policy_t *p;
+ const char *rule_type;
+ char *diff_char = "", *s = NULL, *perm_name, *cond_expr = NULL;
+ size_t i, len = 0;
+ int show_perm_sym = 0, error;
+ if (diff == NULL || avrule == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (pa->form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ diff_char = "+";
+ p = diff->mod_pol;
+ break;
+ }
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ diff_char = "-";
+ p = diff->orig_pol;
+ break;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ diff_char = "*";
+ p = diff->orig_pol;
+ show_perm_sym = 1;
+ break;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ rule_type = apol_rule_type_to_str(pa->spec);
+ if (apol_str_appendf(&s, &len, "%s %s %s %s : %s {", diff_char, rule_type, pa->source, pa->target, pa->cls) < 0) {
+ error = errno;
+ goto err;
+ }
+ for (i = 0; pa->unmodified_perms != NULL && i < apol_vector_get_size(pa->unmodified_perms); i++) {
+ perm_name = (char *)apol_vector_get_element(pa->unmodified_perms, i);
+ if (apol_str_appendf(&s, &len, " %s", perm_name) < 0) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (i = 0; pa->added_perms != NULL && i < apol_vector_get_size(pa->added_perms); i++) {
+ perm_name = (char *)apol_vector_get_element(pa->added_perms, i);
+ if (apol_str_appendf(&s, &len, " %s%s", (show_perm_sym ? "+" : ""), perm_name) < 0) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (i = 0; pa->removed_perms != NULL && i < apol_vector_get_size(pa->removed_perms); i++) {
+ perm_name = (char *)apol_vector_get_element(pa->removed_perms, i);
+ if (apol_str_appendf(&s, &len, " %s%s", (show_perm_sym ? "-" : ""), perm_name) < 0) {
+ error = errno;
+ goto err;
+ }
+ }
+ if (apol_str_append(&s, &len, " };") < 0) {
+ error = errno;
+ goto err;
+ }
+ if (pa->cond != NULL) {
+ if ((cond_expr = apol_cond_expr_render(p, pa->cond)) == NULL) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&s, &len, " [%s]:%s", cond_expr, (pa->branch ? "TRUE" : "FALSE")) < 0) {
+ error = errno;
+ goto err;
+ }
+ free(cond_expr);
+ }
+ return s;
+ err:
+ free(s);
+ free(cond_expr);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+}
+
+/**
+ * Sort poldiff_avrule diff results in a mostly alphabetical order.
+ */
+static int poldiff_avrule_cmp(const void *x, const void *y, void *data __attribute__ ((unused)))
+{
+ const poldiff_avrule_t *a = (const poldiff_avrule_t *)x;
+ const poldiff_avrule_t *b = (const poldiff_avrule_t *)y;
+ int compval;
+ if (a->spec != b->spec) {
+ const char *rule_type1 = apol_rule_type_to_str(a->spec);
+ const char *rule_type2 = apol_rule_type_to_str(b->spec);
+ compval = strcmp(rule_type1, rule_type2);
+ if (compval != 0) {
+ return compval;
+ }
+ }
+ if ((compval = strcmp(a->source, b->source)) != 0) {
+ return compval;
+ }
+ if ((compval = strcmp(a->target, b->target)) != 0) {
+ return compval;
+ }
+ if ((compval = strcmp(a->cls, b->cls)) != 0) {
+ return compval;
+ }
+ if (a->cond != b->cond) {
+ return (char *)a->cond - (char *)b->cond;
+ }
+ /* sort true branch before false branch */
+ return b->branch - a->branch;
+}
+
+/**
+ * Get the vector of av rule differences from the av rule difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the av
+ * rule difference summary.
+ * @param idx Index into the avrule differences specifying which
+ * avrule type to get, one of AVRULE_OFFSET_ALLOW, etc.
+ *
+ * @return A vector of elements of type poldiff_avrule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+static const apol_vector_t *poldiff_get_avrule_vector(const poldiff_t * diff, avrule_offset_e idx)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (diff->avrule_diffs[idx]->diffs_sorted == 0) {
+ apol_vector_sort(diff->avrule_diffs[idx]->diffs, poldiff_avrule_cmp, NULL);
+ diff->avrule_diffs[idx]->diffs_sorted = 1;
+ }
+ return diff->avrule_diffs[idx]->diffs;
+}
+
+const apol_vector_t *poldiff_get_avrule_vector_allow(const poldiff_t * diff)
+{
+ return poldiff_get_avrule_vector(diff, AVRULE_OFFSET_ALLOW);
+}
+
+const apol_vector_t *poldiff_get_avrule_vector_auditallow(const poldiff_t * diff)
+{
+ return poldiff_get_avrule_vector(diff, AVRULE_OFFSET_AUDITALLOW);
+}
+
+const apol_vector_t *poldiff_get_avrule_vector_dontaudit(const poldiff_t * diff)
+{
+ return poldiff_get_avrule_vector(diff, AVRULE_OFFSET_DONTAUDIT);
+}
+
+const apol_vector_t *poldiff_get_avrule_vector_neverallow(const poldiff_t * diff)
+{
+ return poldiff_get_avrule_vector(diff, AVRULE_OFFSET_NEVERALLOW);
+}
+
+poldiff_form_e poldiff_avrule_get_form(const void *avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_avrule_t *)avrule)->form;
+}
+
+uint32_t poldiff_avrule_get_rule_type(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return avrule->spec;
+}
+
+const char *poldiff_avrule_get_source_type(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return avrule->source;
+}
+
+const char *poldiff_avrule_get_target_type(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return avrule->target;
+}
+
+const char *poldiff_avrule_get_object_class(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return avrule->cls;
+}
+
+void poldiff_avrule_get_cond(const poldiff_t * diff, const poldiff_avrule_t * avrule,
+ const qpol_cond_t ** cond, uint32_t * which_list, const apol_policy_t ** p)
+{
+ if (diff == NULL || avrule == NULL || cond == NULL || p == NULL) {
+ errno = EINVAL;
+ return;
+ }
+ *cond = avrule->cond;
+ if (*cond == NULL) {
+ *which_list = 1;
+ *p = NULL;
+ } else if (avrule->form == POLDIFF_FORM_ADDED || avrule->form == POLDIFF_FORM_ADD_TYPE) {
+ *which_list = avrule->branch;
+ *p = diff->mod_pol;
+ } else {
+ *which_list = avrule->branch;
+ *p = diff->orig_pol;
+ }
+}
+
+const apol_vector_t *poldiff_avrule_get_unmodified_perms(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avrule->unmodified_perms;
+}
+
+const apol_vector_t *poldiff_avrule_get_added_perms(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avrule->added_perms;
+}
+
+const apol_vector_t *poldiff_avrule_get_removed_perms(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avrule->removed_perms;
+}
+
+const apol_vector_t *poldiff_avrule_get_orig_line_numbers(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avrule->orig_linenos;
+}
+
+const apol_vector_t *poldiff_avrule_get_mod_line_numbers(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avrule->mod_linenos;
+}
+
+/**
+ * Get the line numbers from an array of qpol_avrule_t that contain
+ * the given permission.
+ */
+static apol_vector_t *avrule_get_line_numbers_for_perm(const poldiff_t * diff, const char *perm, const qpol_policy_t * q,
+ qpol_avrule_t ** rules, const size_t num_rules)
+{
+ apol_vector_t *v = NULL;
+ qpol_iterator_t *syn_iter = NULL, *perm_iter = NULL;
+ size_t i;
+ int error = 0;
+
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (i = 0; i < num_rules; i++) {
+ if (qpol_avrule_get_syn_avrule_iter(q, rules[i], &syn_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(syn_iter); qpol_iterator_next(syn_iter)) {
+ qpol_syn_avrule_t *syn_rule;
+ qpol_iterator_get_item(syn_iter, (void **)&syn_rule);
+ if (qpol_syn_avrule_get_perm_iter(q, syn_rule, &perm_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ char *syn_perm;
+ qpol_iterator_get_item(perm_iter, (void **)&syn_perm);
+ if (strcmp(perm, syn_perm) == 0) {
+ unsigned long lineno;
+ qpol_syn_avrule_get_lineno(q, syn_rule, &lineno);
+ if (apol_vector_append(v, (void *)lineno) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ }
+ break;
+ }
+ }
+ qpol_iterator_destroy(&perm_iter);
+ }
+ qpol_iterator_destroy(&syn_iter);
+ }
+ apol_vector_sort_uniquify(v, NULL, NULL);
+ cleanup:
+ qpol_iterator_destroy(&syn_iter);
+ qpol_iterator_destroy(&perm_iter);
+ if (error != 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+apol_vector_t *poldiff_avrule_get_orig_line_numbers_for_perm(const poldiff_t * diff, const poldiff_avrule_t * avrule,
+ const char *perm)
+{
+ if (diff == NULL || avrule == NULL || perm == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ if (!diff->line_numbers_enabled || avrule->form == POLDIFF_FORM_ADDED || avrule->form == POLDIFF_FORM_ADD_TYPE) {
+ return NULL;
+ }
+ if (avrule->num_orig_rules == 0) {
+ return NULL;
+ }
+ return avrule_get_line_numbers_for_perm(diff, perm, diff->orig_qpol, avrule->orig_rules, avrule->num_orig_rules);
+}
+
+apol_vector_t *poldiff_avrule_get_mod_line_numbers_for_perm(const poldiff_t * diff, const poldiff_avrule_t * avrule,
+ const char *perm)
+{
+ if (diff == NULL || avrule == NULL || perm == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ if (!diff->line_numbers_enabled || avrule->form == POLDIFF_FORM_REMOVED || avrule->form == POLDIFF_FORM_REMOVE_TYPE) {
+ return NULL;
+ }
+ if (avrule->num_mod_rules == 0) {
+ return NULL;
+ }
+ return avrule_get_line_numbers_for_perm(diff, perm, diff->mod_qpol, avrule->mod_rules, avrule->num_mod_rules);
+}
+
+/******************** protected functions below ********************/
+
+/**
+ * Free all space used by a poldiff_avrule_t, including the pointer
+ * itself. Does nothing if the pointer is already NULL.
+ *
+ * @param elem Pointer to a poldiff_avrule_t.
+ */
+static void poldiff_avrule_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_avrule_t *a = (poldiff_avrule_t *) elem;
+ apol_vector_destroy(&a->unmodified_perms);
+ apol_vector_destroy(&a->added_perms);
+ apol_vector_destroy(&a->removed_perms);
+ apol_vector_destroy(&a->orig_linenos);
+ apol_vector_destroy(&a->mod_linenos);
+ free(a->orig_rules);
+ free(a->mod_rules);
+ free(a);
+ }
+}
+
+poldiff_avrule_summary_t *avrule_create(void)
+{
+ poldiff_avrule_summary_t *rs = calloc(1, sizeof(*rs));
+ if (rs == NULL) {
+ return NULL;
+ }
+ if ((rs->diffs = apol_vector_create(poldiff_avrule_free)) == NULL) {
+ avrule_destroy(&rs);
+ return NULL;
+ }
+ return rs;
+}
+
+void avrule_destroy(poldiff_avrule_summary_t ** rs)
+{
+ if (rs != NULL && *rs != NULL) {
+ apol_vector_destroy(&(*rs)->diffs);
+ free(*rs);
+ *rs = NULL;
+ }
+}
+
+/**
+ * Reset the state of an AV rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @param idx Index into the avrule diffs array indicating which rule
+ * type to reset, one of AVRULE_OFFSET_ALLOW, etc.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+static int avrule_reset(poldiff_t * diff, avrule_offset_e idx)
+{
+ int error = 0;
+
+ avrule_destroy(&diff->avrule_diffs[idx]);
+ diff->avrule_diffs[idx] = avrule_create();
+ if (diff->avrule_diffs[idx] == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+int avrule_reset_allow(poldiff_t * diff)
+{
+ return avrule_reset(diff, AVRULE_OFFSET_ALLOW);
+}
+
+int avrule_reset_auditallow(poldiff_t * diff)
+{
+ return avrule_reset(diff, AVRULE_OFFSET_AUDITALLOW);
+}
+
+int avrule_reset_dontaudit(poldiff_t * diff)
+{
+ return avrule_reset(diff, AVRULE_OFFSET_DONTAUDIT);
+}
+
+int avrule_reset_neverallow(poldiff_t * diff)
+{
+ return avrule_reset(diff, AVRULE_OFFSET_NEVERALLOW);
+}
+
+static void avrule_free_item(void *item)
+{
+ pseudo_avrule_t *a = (pseudo_avrule_t *) item;
+ if (item != NULL) {
+ free(a->perms);
+ free(a->rules);
+ free(a);
+ }
+}
+
+/**
+ * Apply an ordering scheme to two pseudo-av rules.
+ *
+ * <ul>
+ * <li>Sort by target pseudo-type value,
+ * <li>Then by source pseudo-type value,
+ * <li>Then by object class's BST's pointer value,
+ * <li>Then by rule specified (allow, neverallow, etc.),
+ * <li>Then choose unconditional rules over conditional rules,
+ * <li>Then by conditional expression's BST's boolean pointer value.
+ * </ul>
+ *
+ * If this function is being used for sorting (via avrule_get_items())
+ * then sort by truth value, and then by branch (true branch, then
+ * false branch). Otherwise, when comparing rules (via avrule_comp())
+ * then by truth value, inverting rule2's value if in the other
+ * branch.
+ */
+static int pseudo_avrule_comp(const pseudo_avrule_t * rule1, const pseudo_avrule_t * rule2, int is_sorting)
+{
+ size_t i;
+ uint32_t bool_val;
+ if (rule1->target != rule2->target) {
+ return rule1->target - rule2->target;
+ }
+ if (rule1->source != rule2->source) {
+ return rule1->source - rule2->source;
+ }
+ if (rule1->cls != rule2->cls) {
+ return (int)(rule1->cls - rule2->cls);
+ }
+ if (rule1->spec != rule2->spec) {
+ return rule1->spec - rule2->spec;
+ }
+ if (rule1->bools[0] == NULL && rule2->bools[0] == NULL) {
+ /* both rules are unconditional */
+ return 0;
+ } else if (rule1->bools[0] == NULL && rule2->bools[0] != NULL) {
+ /* unconditional rules come before conditional */
+ return -1;
+ } else if (rule1->bools[0] != NULL && rule2->bools[0] == NULL) {
+ /* unconditional rules come before conditional */
+ return 1;
+ }
+ for (i = 0; i < (sizeof(rule1->bools) / sizeof(rule1->bools[0])); i++) {
+ if (rule1->bools[i] != rule2->bools[i]) {
+ return (int)(rule1->bools[i] - rule2->bools[i]);
+ }
+ }
+ if (is_sorting) {
+ if (rule1->branch != rule2->branch) {
+ return rule1->branch - rule2->branch;
+ }
+ return (int)rule1->bool_val - (int)rule2->bool_val;
+ } else {
+ if (rule1->branch == rule2->branch) {
+ bool_val = rule2->bool_val;
+ } else {
+ bool_val = ~rule2->bool_val;
+ }
+ if (rule1->bool_val < bool_val) {
+ return -1;
+ } else if (rule1->bool_val > bool_val) {
+ return 1;
+ }
+ return 0;
+ }
+}
+
+static int avrule_bst_comp(const void *x, const void *y, void *data __attribute__ ((unused)))
+{
+ const pseudo_avrule_t *r1 = (const pseudo_avrule_t *)x;
+ const pseudo_avrule_t *r2 = (const pseudo_avrule_t *)y;
+ return pseudo_avrule_comp(r1, r2, 1);
+}
+
+/**
+ * Given a conditional expression, convert its booleans to a sorted
+ * array of pseudo-boolean values, assign that array to the
+ * pseudo-avrule key, and then derive the truth table.
+ *
+ * @param diff Policy difference structure.
+ * @param p Policy containing conditional.
+ * @param cond Conditional expression to convert.
+ * @param key Location to write converted expression.
+ */
+static int avrule_build_cond(poldiff_t * diff, const apol_policy_t * p, const qpol_cond_t * cond, pseudo_avrule_t * key)
+{
+ qpol_iterator_t *iter = NULL;
+ qpol_cond_expr_node_t *node;
+ uint32_t expr_type, truthiness;
+ qpol_bool_t *bools[5] = { NULL, NULL, NULL, NULL, NULL }, *qbool;
+ size_t i, j;
+ size_t num_bools = 0;
+ const char *bool_name;
+ char *pseudo_bool, *t;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1, error = 0, compval;
+ if (qpol_cond_get_expr_node_iter(q, cond, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&node) < 0 || qpol_cond_expr_node_get_expr_type(q, node, &expr_type) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (expr_type != QPOL_COND_EXPR_BOOL) {
+ continue;
+ }
+ if (qpol_cond_expr_node_get_bool(q, node, &qbool) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (i = 0; i < num_bools; i++) {
+ if (bools[i] == qbool) {
+ break;
+ }
+ }
+ if (i >= num_bools) {
+ assert(i < 5);
+ bools[i] = qbool;
+ num_bools++;
+ }
+ }
+ for (i = 0; i < num_bools; i++) {
+ if (qpol_bool_get_name(q, bools[i], &bool_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_bst_get_element(diff->bool_bst, (void *)bool_name, NULL, (void **)&pseudo_bool) < 0) {
+ error = EBADRQC; /* should never get here */
+ ERR(diff, "%s", strerror(error));
+ assert(0);
+ goto cleanup;
+ }
+ key->bools[i] = pseudo_bool;
+ }
+
+ /* bubble sorth the pseudo bools (not bad because there are at
+ * most five elements */
+ for (i = num_bools; i > 1; i--) {
+ for (j = 1; j < i; j++) {
+ compval = strcmp(key->bools[j - 1], key->bools[j]);
+ if (compval > 0) {
+ t = key->bools[j];
+ key->bools[j] = key->bools[j - 1];
+ key->bools[j - 1] = t;
+ qbool = bools[j];
+ bools[j] = bools[j - 1];
+ bools[j - 1] = qbool;
+ }
+ }
+ }
+
+ /* now compute the truth table for the booleans */
+ key->bool_val = 0;
+ for (i = 0; i < 32; i++) {
+ for (j = 0; j < num_bools; j++) {
+ int state = ((i & (1 << j)) ? 1 : 0);
+ if (qpol_bool_set_state_no_eval(q, bools[j], state) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if (qpol_cond_eval(q, cond, &truthiness) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ key->bool_val = (key->bool_val << 1) | truthiness;
+ }
+
+ key->cond = cond;
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Bubble sort the permissions within a pseudo-avrule, sorted by
+ * pointer value. (Bubble-sort is fine because the number of
+ * permissions will usually be less than 10.) Then uniquify the list.
+ *
+ * @param key Rule whose permissions to sort.
+ */
+static void sort_and_uniquify_perms(pseudo_avrule_t * key)
+{
+ size_t i, j;
+ char *t;
+ for (i = key->num_perms; i > 1; i--) {
+ for (j = 1; j < i; j++) {
+ if (key->perms[j - 1] > key->perms[j]) {
+ t = key->perms[j];
+ key->perms[j] = key->perms[j - 1];
+ key->perms[j - 1] = t;
+ }
+ }
+ }
+ for (i = 1; i < key->num_perms; i++) {
+ if (key->perms[i] == key->perms[i - 1]) {
+ memmove(key->perms + i, key->perms + i + 1, (key->num_perms - i - 1) * sizeof(key->perms[0]));
+ key->num_perms--;
+ }
+ }
+}
+
+/**
+ * Given a rule, construct a new pseudo-avrule and insert it into the
+ * BST if not already there.
+ *
+ * @param diff Policy difference structure.
+ * @param p Policy from which the rule came.
+ * @param rule AV rule to insert.
+ * @param source Source pseudo-type value.
+ * @param target Target pseudo-type value.
+ * @param b BST containing pseudo-avrules.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int avrule_add_to_bst(poldiff_t * diff, const apol_policy_t * p,
+ const qpol_avrule_t * rule, uint32_t source, uint32_t target, apol_bst_t * b)
+{
+ pseudo_avrule_t *key, *inserted_key;
+ const qpol_class_t *obj_class;
+ qpol_iterator_t *perm_iter = NULL;
+ const char *class_name;
+ char *perm_name, *pseudo_perm, **t;
+ size_t num_perms;
+ const qpol_cond_t *cond;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1, error = 0, compval;
+ if ((key = calloc(1, sizeof(*key))) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (qpol_avrule_get_rule_type(q, rule, &(key->spec)) < 0 ||
+ qpol_avrule_get_object_class(q, rule, &obj_class) < 0 ||
+ qpol_avrule_get_perm_iter(q, rule, &perm_iter) < 0 || qpol_avrule_get_cond(q, rule, &cond) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (qpol_class_get_name(q, obj_class, &class_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_bst_get_element(diff->class_bst, (void *)class_name, NULL, (void **)&key->cls) < 0) {
+ error = EBADRQC; /* should never get here */
+ ERR(diff, "%s", strerror(error));
+ assert(0);
+ goto cleanup;
+ }
+ key->source = source;
+ key->target = target;
+ if (cond != NULL && (qpol_avrule_get_which_list(q, rule, &(key->branch)) < 0 || avrule_build_cond(diff, p, cond, key) < 0)) {
+ error = errno;
+ goto cleanup;
+ }
+
+ /* insert this pseudo into the tree if not already there */
+ if ((compval = apol_bst_insert_and_get(b, (void **)&key, NULL)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ inserted_key = key;
+ key = NULL;
+
+ /* append and uniquify this rule's permissions */
+ if (qpol_iterator_get_size(perm_iter, &num_perms) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((t = realloc(inserted_key->perms, (inserted_key->num_perms + num_perms) * sizeof(*t))) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ inserted_key->perms = t;
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ if (qpol_iterator_get_item(perm_iter, (void *)&perm_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_bst_get_element(diff->perm_bst, perm_name, NULL, (void **)&pseudo_perm) < 0) {
+ error = EBADRQC; /* should never get here */
+ ERR(diff, "%s", strerror(error));
+ assert(0);
+ free(perm_name);
+ goto cleanup;
+ }
+ free(perm_name);
+ inserted_key->perms[(inserted_key->num_perms)++] = pseudo_perm;
+ }
+ sort_and_uniquify_perms(inserted_key);
+
+ /* store the rule pointer, to be used for showing line numbers */
+ if (qpol_policy_has_capability(q, QPOL_CAP_LINE_NUMBERS)) {
+ const qpol_avrule_t **a = realloc(inserted_key->rules,
+ (inserted_key->num_rules + 1) * sizeof(*a));
+ if (a == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ inserted_key->rules = a;
+ inserted_key->rules[inserted_key->num_rules++] = rule;
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&perm_iter);
+ if (retval < 0) {
+ avrule_free_item(key);
+ }
+ errno = error;
+ return retval;
+}
+
+/**
+ * Given a rule, expand its source and target types into individual
+ * pseudo-type values. Then add the expanded rules to the BST. This
+ * is needed for when the source and/or target is an attribute.
+ *
+ * @param diff Policy difference structure.
+ * @param p Policy from which the rule came.
+ * @param rule AV rule to insert.
+ * @param b BST containing pseudo-avrules.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int avrule_expand(poldiff_t * diff, const apol_policy_t * p, const qpol_avrule_t * rule, apol_bst_t * b)
+{
+ const qpol_type_t *source, *orig_target, *target;
+ unsigned char source_attr, target_attr;
+ qpol_iterator_t *source_iter = NULL, *target_iter = NULL;
+ uint32_t source_val, target_val;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int which = (p == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD);
+ int retval = -1, error = 0;
+ if (qpol_avrule_get_source_type(q, rule, &source) < 0 ||
+ qpol_avrule_get_target_type(q, rule, &orig_target) < 0 ||
+ qpol_type_get_isattr(q, source, &source_attr) < 0 || qpol_type_get_isattr(q, orig_target, &target_attr)) {
+ error = errno;
+ goto cleanup;
+ }
+#ifdef SETOOLS_DEBUG
+ const char *orig_source_name, *orig_target_name;
+ qpol_type_get_name(q, source, &orig_source_name);
+ qpol_type_get_name(q, orig_target, &orig_target_name);
+#endif
+
+ if (source_attr) {
+ if (qpol_type_get_type_iter(q, source, &source_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ /* handle situation where a rule has as its source an
+ * attribute without any types */
+ if (qpol_iterator_end(source_iter)) {
+ retval = 0;
+ goto cleanup;
+ }
+ }
+ do {
+ if (source_attr) {
+ if (qpol_iterator_get_item(source_iter, (void **)&source) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ qpol_iterator_next(source_iter);
+ }
+ if (target_attr) {
+ if (qpol_type_get_type_iter(q, orig_target, &target_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ /* handle situation where a rule has as its
+ * target an attribute without any types */
+ if (qpol_iterator_end(target_iter)) {
+ retval = 0;
+ goto cleanup;
+ }
+ } else {
+ target = orig_target;
+ }
+ do {
+ if (target_attr) {
+ if (qpol_iterator_get_item(target_iter, (void **)&target) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ qpol_iterator_next(target_iter);
+ }
+#ifdef SETOOLS_DEBUG
+ const char *n1, *n2;
+ qpol_type_get_name(q, source, &n1);
+ qpol_type_get_name(q, target, &n2);
+#endif
+ if ((source_val = type_map_lookup(diff, source, which)) == 0 ||
+ (target_val = type_map_lookup(diff, target, which)) == 0 ||
+ avrule_add_to_bst(diff, p, rule, source_val, target_val, b) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ } while (target_attr && !qpol_iterator_end(target_iter));
+ qpol_iterator_destroy(&target_iter);
+ } while (source_attr && !qpol_iterator_end(source_iter));
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&source_iter);
+ qpol_iterator_destroy(&target_iter);
+ errno = error;
+ return retval;
+}
+
+/**
+ * Get a vector of avrules from the given policy, sorted. This
+ * function will remap source and target types to their pseudo-type
+ * value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ * @param which Kind of rule to get, one of QPOL_RULE_ALLOW, etc.
+ *
+ * @return A newly allocated vector of all av rules (of type
+ * pseudo_avrule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+static apol_vector_t *avrule_get_items(poldiff_t * diff, const apol_policy_t * policy, const unsigned int which)
+{
+ apol_vector_t *bools = NULL, *bool_states = NULL;
+ size_t i, num_rules, j;
+ apol_bst_t *b = NULL;
+ apol_vector_t *v = NULL;
+ qpol_iterator_t *iter = NULL;
+ const qpol_avrule_t *rule;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int retval = -1, error = 0;
+
+ /* special case: if getting neverallow rules if the policy
+ does not support it, then return an empty vector */
+ if (which == QPOL_RULE_NEVERALLOW && !qpol_policy_has_capability(q, QPOL_CAP_NEVERALLOW)) {
+ v = apol_vector_create_with_capacity(1, avrule_free_item);
+ if (v == NULL) {
+ ERR(diff, "%s", strerror(error));
+ }
+ return v;
+ }
+
+ if (poldiff_build_bsts(diff) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+
+ /* store original boolean values */
+ if (apol_bool_get_by_query(policy, NULL, &bools) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((bool_states = apol_vector_create_with_capacity(apol_vector_get_size(bools), NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(bools); i++) {
+ qpol_bool_t *qbool = apol_vector_get_element(bools, i);
+ int state;
+ if (qpol_bool_get_state(q, qbool, &state) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_append(bool_states, (void *)((size_t) state)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if ((b = apol_bst_create(avrule_bst_comp, avrule_free_item)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (qpol_policy_get_avrule_iter(q, which, &iter) < 0) {
+
+ error = errno;
+ goto cleanup;
+ }
+ qpol_iterator_get_size(iter, &num_rules);
+ for (j = 0; !qpol_iterator_end(iter); qpol_iterator_next(iter), j++) {
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0 || avrule_expand(diff, policy, rule, b) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (!(j % 1024)) {
+ int percent = 50 * j / num_rules + (policy == diff->mod_pol ? 50 : 0);
+ INFO(diff, "Computing AV rule difference: %02d%% complete", percent);
+ }
+ }
+ if ((v = apol_bst_get_vector(b, 1)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ /* restore boolean states */
+ for (i = 0; i < apol_vector_get_size(bools); i++) {
+ qpol_bool_t *qbool = apol_vector_get_element(bools, i);
+ int state = (int)((size_t) apol_vector_get_element(bool_states, i));
+ qpol_bool_set_state_no_eval(q, qbool, state);
+ }
+ qpol_policy_reevaluate_conds(q);
+ apol_vector_destroy(&bools);
+ apol_vector_destroy(&bool_states);
+ apol_bst_destroy(&b);
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+apol_vector_t *avrule_get_items_allow(poldiff_t * diff, const apol_policy_t * policy)
+{
+ return avrule_get_items(diff, policy, QPOL_RULE_ALLOW);
+}
+
+apol_vector_t *avrule_get_items_auditallow(poldiff_t * diff, const apol_policy_t * policy)
+{
+ return avrule_get_items(diff, policy, QPOL_RULE_AUDITALLOW);
+}
+
+apol_vector_t *avrule_get_items_dontaudit(poldiff_t * diff, const apol_policy_t * policy)
+{
+ return avrule_get_items(diff, policy, QPOL_RULE_DONTAUDIT);
+}
+
+apol_vector_t *avrule_get_items_neverallow(poldiff_t * diff, const apol_policy_t * policy)
+{
+ return avrule_get_items(diff, policy, QPOL_RULE_NEVERALLOW);
+}
+
+int avrule_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused)))
+{
+ const pseudo_avrule_t *r1 = (const pseudo_avrule_t *)x;
+ const pseudo_avrule_t *r2 = (const pseudo_avrule_t *)y;
+ return pseudo_avrule_comp(r1, r2, 0);
+}
+
+/**
+ * Allocate and return a new avrule difference object. If the
+ * pseudo-avrule's source and/or target expands to multiple read
+ * types, then just choose the first one for display.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param rule Pseudo avrule that changed.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling poldiff_avrule_free() upon
+ * the returned value.
+ */
+static poldiff_avrule_t *make_avdiff(poldiff_t * diff, poldiff_form_e form, pseudo_avrule_t * rule)
+{
+ poldiff_avrule_t *pa = NULL;
+ const char *n1, *n2;
+ int error = 0;
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ n1 = type_map_get_name(diff, rule->source, POLDIFF_POLICY_MOD);
+ n2 = type_map_get_name(diff, rule->target, POLDIFF_POLICY_MOD);
+ } else {
+ n1 = type_map_get_name(diff, rule->source, POLDIFF_POLICY_ORIG);
+ n2 = type_map_get_name(diff, rule->target, POLDIFF_POLICY_ORIG);
+ }
+ assert(n1 != NULL && n2 != NULL);
+ if ((pa = calloc(1, sizeof(*pa))) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ pa->spec = rule->spec;
+ pa->source = n1;
+ pa->target = n2;
+ pa->cls = rule->cls;
+ pa->form = form;
+ pa->cond = rule->cond;
+ pa->branch = rule->branch;
+ cleanup:
+ if (error != 0) {
+ poldiff_avrule_free(pa);
+ errno = error;
+ return NULL;
+ }
+ return pa;
+}
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-av rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ * @param idx Index into the avrule differences specifying into which
+ * to place the constructed pseudo-av rule.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+static int avrule_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item, avrule_offset_e idx)
+{
+ pseudo_avrule_t *rule = (pseudo_avrule_t *) item;
+ poldiff_avrule_t *pa = NULL;
+ const apol_vector_t *v1, *v2;
+ apol_vector_t **target;
+ apol_policy_t *p;
+ size_t i;
+ int retval = -1, error = errno;
+
+ /* check if form should really become ADD_TYPE / REMOVE_TYPE,
+ * by seeing if the /other/ policy's reverse lookup is
+ * empty */
+ if (form == POLDIFF_FORM_ADDED) {
+ if ((v1 = type_map_lookup_reverse(diff, rule->source, POLDIFF_POLICY_ORIG)) == NULL ||
+ (v2 = type_map_lookup_reverse(diff, rule->target, POLDIFF_POLICY_ORIG)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) {
+ form = POLDIFF_FORM_ADD_TYPE;
+ }
+ p = diff->mod_pol;
+ } else {
+ if ((v1 = type_map_lookup_reverse(diff, rule->source, POLDIFF_POLICY_MOD)) == NULL ||
+ (v2 = type_map_lookup_reverse(diff, rule->target, POLDIFF_POLICY_MOD)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) {
+ form = POLDIFF_FORM_REMOVE_TYPE;
+ }
+ p = diff->orig_pol;
+ }
+
+ pa = make_avdiff(diff, form, rule);
+ if (pa == NULL) {
+ return -1;
+ }
+
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ if ((pa->removed_perms = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ (pa->unmodified_perms = apol_vector_create_with_capacity(1, NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ target = &pa->added_perms;
+ } else {
+ if ((pa->added_perms = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ (pa->unmodified_perms = apol_vector_create_with_capacity(1, NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ target = &pa->removed_perms;
+ }
+ if ((*target = apol_vector_create_with_capacity(rule->num_perms, NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < rule->num_perms; i++) {
+ if (apol_vector_append(*target, rule->perms[i]) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ apol_vector_sort(*target, apol_str_strcmp, NULL);
+
+ if (qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_LINE_NUMBERS)) {
+ /* calculate line numbers */
+ apol_vector_t *vl = NULL;
+ if ((vl = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ pa->mod_linenos = vl;
+ } else {
+ pa->orig_linenos = vl;
+ }
+
+ /* copy rule pointers for delayed line number claculation */
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ pa->num_mod_rules = rule->num_rules;
+ pa->mod_rules = calloc(rule->num_rules, sizeof(qpol_avrule_t *));
+ if (!pa->mod_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pa->mod_rules, rule->rules, rule->num_rules * sizeof(qpol_avrule_t *));
+ } else {
+ pa->num_orig_rules = rule->num_rules;
+ pa->orig_rules = calloc(rule->num_rules, sizeof(qpol_avrule_t *));
+ if (!pa->orig_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pa->orig_rules, rule->rules, rule->num_rules * sizeof(qpol_avrule_t *));
+ }
+ }
+
+ if (apol_vector_append(diff->avrule_diffs[idx]->diffs, pa) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ diff->avrule_diffs[idx]->num_added++;
+ break;
+ case POLDIFF_FORM_ADD_TYPE:
+ diff->avrule_diffs[idx]->num_added_type++;
+ break;
+ case POLDIFF_FORM_REMOVED:
+ diff->avrule_diffs[idx]->num_removed++;
+ break;
+ case POLDIFF_FORM_REMOVE_TYPE:
+ diff->avrule_diffs[idx]->num_removed_type++;
+ break;
+ default:
+ error = EBADRQC; /* should never get here */
+ ERR(diff, "%s", strerror(error));
+ assert(0);
+ goto cleanup;
+ }
+ diff->avrule_diffs[idx]->diffs_sorted = 0;
+ retval = 0;
+ cleanup:
+ if (retval < 0) {
+ poldiff_avrule_free(pa);
+ }
+ errno = error;
+ return retval;
+}
+
+int avrule_new_diff_allow(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ return avrule_new_diff(diff, form, item, AVRULE_OFFSET_ALLOW);
+}
+
+int avrule_new_diff_auditallow(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ return avrule_new_diff(diff, form, item, AVRULE_OFFSET_AUDITALLOW);
+}
+
+int avrule_new_diff_dontaudit(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ return avrule_new_diff(diff, form, item, AVRULE_OFFSET_DONTAUDIT);
+}
+
+int avrule_new_diff_neverallow(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ return avrule_new_diff(diff, form, item, AVRULE_OFFSET_NEVERALLOW);
+}
+
+/**
+ * Compute the semantic difference of two pseudo-av rules for which
+ * the compare callback returns 0. If a difference is found then
+ * allocate, initialize, and insert a new semantic difference entry
+ * for that pseudo-av rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-av rules and to which to add an entry if needed.
+ * @param x The pseudo-av rule from the original policy.
+ * @param y The pseudo-av rule from the modified policy.
+ * @param idx Index into the avrule differences specifying into which
+ * to place the constructed pseudo-av rule.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+static int avrule_deep_diff(poldiff_t * diff, const void *x, const void *y, avrule_offset_e idx)
+{
+ pseudo_avrule_t *r1 = (pseudo_avrule_t *) x;
+ pseudo_avrule_t *r2 = (pseudo_avrule_t *) y;
+ apol_vector_t *unmodified_perms = NULL, *added_perms = NULL, *removed_perms = NULL;
+ size_t i, j;
+ char *perm1, *perm2;
+ poldiff_avrule_t *pa = NULL;
+ int retval = -1, error = 0;
+
+ if ((unmodified_perms = apol_vector_create(NULL)) == NULL ||
+ (added_perms = apol_vector_create(NULL)) == NULL || (removed_perms = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = j = 0; i < r1->num_perms;) {
+ if (j >= r2->num_perms)
+ break;
+ perm1 = r1->perms[i];
+ perm2 = r2->perms[j];
+ if (perm2 > perm1) {
+ if (apol_vector_append(removed_perms, perm1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (perm1 > perm2) {
+ if (apol_vector_append(added_perms, perm2) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ if (apol_vector_append(unmodified_perms, perm1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ j++;
+ }
+ }
+
+ for (; i < r1->num_perms; i++) {
+ perm1 = r1->perms[i];
+ if (apol_vector_append(removed_perms, perm1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < r2->num_perms; j++) {
+ perm2 = r2->perms[j];
+ if (apol_vector_append(added_perms, perm2) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_get_size(added_perms) > 0 || apol_vector_get_size(removed_perms) > 0) {
+ if ((pa = make_avdiff(diff, POLDIFF_FORM_MODIFIED, r1)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ pa->unmodified_perms = unmodified_perms;
+ pa->added_perms = added_perms;
+ pa->removed_perms = removed_perms;
+ unmodified_perms = NULL;
+ added_perms = NULL;
+ removed_perms = NULL;
+ apol_vector_sort(pa->unmodified_perms, apol_str_strcmp, NULL);
+ apol_vector_sort(pa->added_perms, apol_str_strcmp, NULL);
+ apol_vector_sort(pa->removed_perms, apol_str_strcmp, NULL);
+
+ /* calculate line numbers */
+ if (qpol_policy_has_capability(apol_policy_get_qpol(diff->orig_pol), QPOL_CAP_LINE_NUMBERS)) {
+ if ((pa->orig_linenos = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ /* copy rule pointers for delayed line number claculation */
+ pa->num_orig_rules = r1->num_rules;
+ pa->orig_rules = calloc(r1->num_rules, sizeof(qpol_avrule_t *));
+ if (!pa->orig_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pa->orig_rules, r1->rules, r1->num_rules * sizeof(qpol_avrule_t *));
+ }
+ if (qpol_policy_has_capability(apol_policy_get_qpol(diff->mod_pol), QPOL_CAP_LINE_NUMBERS)) {
+ if ((pa->mod_linenos = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ /* copy rule pointers for delayed line number claculation */
+ pa->num_mod_rules = r2->num_rules;
+ pa->mod_rules = calloc(r2->num_rules, sizeof(qpol_avrule_t *));
+ if (!pa->mod_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pa->mod_rules, r2->rules, r2->num_rules * sizeof(qpol_avrule_t *));
+ }
+ if (apol_vector_append(diff->avrule_diffs[idx]->diffs, pa) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->avrule_diffs[idx]->num_modified++;
+ diff->avrule_diffs[idx]->diffs_sorted = 0;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&unmodified_perms);
+ apol_vector_destroy(&added_perms);
+ apol_vector_destroy(&removed_perms);
+ if (retval != 0) {
+ poldiff_avrule_free(pa);
+ }
+ errno = error;
+ return retval;
+}
+
+int avrule_deep_diff_allow(poldiff_t * diff, const void *x, const void *y)
+{
+ return avrule_deep_diff(diff, x, y, AVRULE_OFFSET_ALLOW);
+}
+
+int avrule_deep_diff_auditallow(poldiff_t * diff, const void *x, const void *y)
+{
+ return avrule_deep_diff(diff, x, y, AVRULE_OFFSET_AUDITALLOW);
+}
+
+int avrule_deep_diff_dontaudit(poldiff_t * diff, const void *x, const void *y)
+{
+ return avrule_deep_diff(diff, x, y, AVRULE_OFFSET_DONTAUDIT);
+}
+
+int avrule_deep_diff_neverallow(poldiff_t * diff, const void *x, const void *y)
+{
+ return avrule_deep_diff(diff, x, y, AVRULE_OFFSET_NEVERALLOW);
+}
+
+int avrule_enable_line_numbers(poldiff_t * diff, avrule_offset_e idx)
+{
+ const apol_vector_t *av = NULL;
+ poldiff_avrule_t *avrule = NULL;
+ size_t i, j;
+ qpol_iterator_t *iter = NULL;
+ qpol_syn_avrule_t *sav = NULL;
+ int error = 0;
+ unsigned long lineno = 0;
+
+ av = poldiff_get_avrule_vector(diff, idx);
+
+ for (i = 0; i < apol_vector_get_size(av); i++) {
+ avrule = apol_vector_get_element(av, i);
+ if (apol_vector_get_size(avrule->mod_linenos) || apol_vector_get_size(avrule->orig_linenos))
+ continue;
+ for (j = 0; j < avrule->num_orig_rules; j++) {
+ if (qpol_avrule_get_syn_avrule_iter(diff->orig_qpol, avrule->orig_rules[j], &iter)) {
+ error = errno;
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&sav) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_syn_avrule_get_lineno(diff->orig_qpol, sav, &lineno) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (apol_vector_append(avrule->orig_linenos, (void *)lineno) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_sort_uniquify(avrule->orig_linenos, NULL, NULL);
+ for (j = 0; j < avrule->num_mod_rules; j++) {
+ if (qpol_avrule_get_syn_avrule_iter(diff->mod_qpol, avrule->mod_rules[j], &iter)) {
+ error = errno;
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&sav) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_syn_avrule_get_lineno(diff->mod_qpol, sav, &lineno) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (apol_vector_append(avrule->mod_linenos, (void *)lineno) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_sort_uniquify(avrule->mod_linenos, NULL, NULL);
+ }
+ return 0;
+ err:
+ qpol_iterator_destroy(&iter);
+ return -1;
+}
diff --git a/libpoldiff/src/avrule_internal.h b/libpoldiff/src/avrule_internal.h
new file mode 100644
index 0000000..9bdf517
--- /dev/null
+++ b/libpoldiff/src/avrule_internal.h
@@ -0,0 +1,296 @@
+/**
+ * @file
+ * Protected interface for AV rule differences.
+ *
+ * @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 POLDIFF_AVRULE_INTERNAL_H
+#define POLDIFF_AVRULE_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_avrule_summary poldiff_avrule_summary_t;
+
+/**
+ * Allocate and return a new poldiff_terule_summary_t object, used by
+ * AV rule searches.
+ *
+ * @return A new rule summary. The caller must call avrule_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_avrule_summary_t *avrule_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_avrule_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param rs Reference to an rule summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void avrule_destroy(poldiff_avrule_summary_t ** rs);
+
+/**
+ * Reset the state of AV allow rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int avrule_reset_allow(poldiff_t * diff);
+
+/**
+ * Reset the state of AV auditallow rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int avrule_reset_auditallow(poldiff_t * diff);
+
+/**
+ * Reset the state of AV dontaudit rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int avrule_reset_dontaudit(poldiff_t * diff);
+
+/**
+ * Reset the state of AV neverallow rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int avrule_reset_neverallow(poldiff_t * diff);
+
+/**
+ * Get a vector of AV allow rules from the given policy, sorted. This
+ * function will remap source and target types to their pseudo-type
+ * value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of av allow rules (of type
+ * pseudo_avrule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *avrule_get_items_allow(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Get a vector of AV auditallow rules from the given policy, sorted.
+ * This function will remap source and target types to their
+ * pseudo-type value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of av auditallow rules (of type
+ * pseudo_avrule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *avrule_get_items_auditallow(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Get a vector of AV dontaudit rules from the given policy, sorted.
+ * This function will remap source and target types to their
+ * pseudo-type value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of av dontaudit rules (of type
+ * pseudo_avrule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *avrule_get_items_dontaudit(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Get a vector of AV neverallow rules from the given policy, sorted.
+ * This function will remap source and target types to their
+ * pseudo-type value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of av neverallow rules (of type
+ * pseudo_avrule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *avrule_get_items_neverallow(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two pseudo_avrule_t objects, determining if they have the
+ * same key (specified + source + target + class + conditional
+ * expression).
+ *
+ * @param x The pseudo-av rule from the original policy.
+ * @param y The pseudo-av rule from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if av rule x is respectively less than,
+ * equal to, or greater than av rule y.
+ */
+ int avrule_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-av rule that was originally an allow rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_new_diff_allow(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-av rule that was originally an auditallow rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_new_diff_auditallow(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-av rule that was originally a dontaudit rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_new_diff_dontaudit(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-av rule that was originally a neverallow rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_new_diff_neverallow(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two pseudo-av rules (that were
+ * allow rules) for which the compare callback returns 0. If a
+ * difference is found then allocate, initialize, and insert a new
+ * semantic difference entry for that pseudo-av rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-av rules and to which to add an entry if needed.
+ * @param x The pseudo-av rule from the original policy.
+ * @param y The pseudo-av rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_deep_diff_allow(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Compute the semantic difference of two pseudo-av rules (that were
+ * auditallow rules) for which the compare callback returns 0. If a
+ * difference is found then allocate, initialize, and insert a new
+ * semantic difference entry for that pseudo-av rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-av rules and to which to add an entry if needed.
+ * @param x The pseudo-av rule from the original policy.
+ * @param y The pseudo-av rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_deep_diff_auditallow(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Compute the semantic difference of two pseudo-av rules (that were
+ * dontaudit rules) for which the compare callback returns 0. If a
+ * difference is found then allocate, initialize, and insert a new
+ * semantic difference entry for that pseudo-av rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-av rules and to which to add an entry if needed.
+ * @param x The pseudo-av rule from the original policy.
+ * @param y The pseudo-av rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_deep_diff_dontaudit(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Compute the semantic difference of two pseudo-av rules (that were
+ * neverallow rules) for which the compare callback returns 0. If a
+ * difference is found then allocate, initialize, and insert a new
+ * semantic difference entry for that pseudo-av rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-av rules and to which to add an entry if needed.
+ * @param x The pseudo-av rule from the original policy.
+ * @param y The pseudo-av rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_deep_diff_neverallow(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Iterate through an AV rule difference, filling in its line numbers.
+ *
+ * @param diff Diff structure containing avrule differences.
+ * @param idx Index into the avrule differences specifying which line
+ * number table to enable.
+ *
+ * @return 0 on success, < 0 on errno.
+ */
+ int avrule_enable_line_numbers(poldiff_t * diff, avrule_offset_e idx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_AVRULE_INTERNAL_H */
diff --git a/libpoldiff/src/bool_diff.c b/libpoldiff/src/bool_diff.c
new file mode 100644
index 0000000..3db1e46
--- /dev/null
+++ b/libpoldiff/src/bool_diff.c
@@ -0,0 +1,333 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in booleans.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Randy Wicks rwicks@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 "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <apol/vector.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+struct poldiff_bool_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_bool
+{
+ char *name;
+ poldiff_form_e form;
+ bool state;
+};
+
+void poldiff_bool_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->bool_diffs->num_added;
+ stats[1] = diff->bool_diffs->num_removed;
+ stats[2] = diff->bool_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+char *poldiff_bool_to_string(const poldiff_t * diff, const void *boolean)
+{
+ poldiff_bool_t *b = (poldiff_bool_t *) boolean;
+ size_t len = 0;
+ char *s = NULL;
+ if (diff == NULL || boolean == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (b->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", b->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", b->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf
+ (&s, &len, "* %s (changed from %s)", b->name, (b->state ? "false to true" : "true to false")) < 0) {
+ break;
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_bool_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->bool_diffs->diffs;
+}
+
+const char *poldiff_bool_get_name(const poldiff_bool_t * boolean)
+{
+ if (boolean == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return boolean->name;
+}
+
+poldiff_form_e poldiff_bool_get_form(const void *boolean)
+{
+ if (boolean == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_bool_t *)boolean)->form;
+}
+
+/******************** protected functions ********************/
+
+static void bool_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_bool_t *b = (poldiff_bool_t *) elem;
+ free(b->name);
+ free(b);
+ }
+}
+
+poldiff_bool_summary_t *bool_create(void)
+{
+ poldiff_bool_summary_t *bs = calloc(1, sizeof(*bs));
+ if (bs == NULL) {
+ return NULL;
+ }
+ if ((bs->diffs = apol_vector_create(bool_free)) == NULL) {
+ bool_destroy(&bs);
+ return NULL;
+ }
+ return bs;
+}
+
+void bool_destroy(poldiff_bool_summary_t ** bs)
+{
+ if (bs != NULL && *bs != NULL) {
+ apol_vector_destroy(&(*bs)->diffs);
+ free(*bs);
+ *bs = NULL;
+ }
+}
+
+int bool_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ bool_destroy(&diff->bool_diffs);
+ diff->bool_diffs = bool_create();
+ if (diff->bool_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two bools from the same policy.
+ */
+static int bool_name_comp(const void *x, const void *y, void *arg)
+{
+ qpol_bool_t *c1 = (qpol_bool_t *) x;
+ qpol_bool_t *c2 = (qpol_bool_t *) y;
+ apol_policy_t *p = (apol_policy_t *) arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+ if (qpol_bool_get_name(q, c1, &name1) < 0 || qpol_bool_get_name(q, c2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *bool_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_bool_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create_from_iter(iter, NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, bool_name_comp, (void *)policy);
+ return v;
+}
+
+int bool_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ qpol_bool_t *c1 = (qpol_bool_t *) x;
+ qpol_bool_t *c2 = (qpol_bool_t *) y;
+ const char *name1, *name2;
+ if (qpol_bool_get_name(diff->orig_qpol, c1, &name1) < 0 || qpol_bool_get_name(diff->mod_qpol, c2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new bool difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the bool that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling bool_free() upon the
+ * returned value.
+ */
+static poldiff_bool_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_bool_t *pb;
+ int error;
+ if ((pb = calloc(1, sizeof(*pb))) == NULL || (pb->name = strdup(name)) == NULL) {
+ error = errno;
+ bool_free(pb);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pb->form = form;
+ return pb;
+}
+
+int bool_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ qpol_bool_t *c = (qpol_bool_t *) item;
+ const char *name = NULL;
+ poldiff_bool_t *pb;
+ int error;
+ if ((form == POLDIFF_FORM_ADDED &&
+ qpol_bool_get_name(diff->mod_qpol, c, &name) < 0) ||
+ ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_bool_get_name(diff->orig_qpol, c, &name) < 0))
+ {
+ return -1;
+ }
+ pb = make_diff(diff, form, name);
+ if (pb == NULL) {
+ return -1;
+ }
+ if (apol_vector_append(diff->bool_diffs->diffs, pb) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ bool_free(pb);
+ errno = error;
+ return -1;
+ }
+ if (form == POLDIFF_FORM_ADDED)
+ diff->bool_diffs->num_added++;
+ else
+ diff->bool_diffs->num_removed++;
+ return 0;
+}
+
+int bool_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ qpol_bool_t *b1 = (qpol_bool_t *) x;
+ qpol_bool_t *b2 = (qpol_bool_t *) y;
+ const char *name;
+ int s1, s2;
+ poldiff_bool_t *b = NULL;
+ int retval = -1, error = 0;
+
+ if (qpol_bool_get_name(diff->orig_qpol, b1, &name) < 0 ||
+ qpol_bool_get_state(diff->orig_qpol, b1, &s1) < 0 || qpol_bool_get_state(diff->mod_qpol, b2, &s2) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (s1 != s2) {
+ if ((b = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (s2)
+ b->state = true;
+ else
+ b->state = false;
+ }
+ if (b != NULL) {
+ if (apol_vector_append(diff->bool_diffs->diffs, b) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->bool_diffs->num_modified++;
+ }
+ retval = 0;
+ cleanup:
+ errno = error;
+ return retval;
+}
diff --git a/libpoldiff/src/bool_internal.h b/libpoldiff/src/bool_internal.h
new file mode 100644
index 0000000..744419f
--- /dev/null
+++ b/libpoldiff/src/bool_internal.h
@@ -0,0 +1,122 @@
+/**
+ * @file
+ * Protected interface for boolean differences.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Randy Wicks rwicks@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 POLDIFF_BOOL_INTERNAL_H
+#define POLDIFF_BOOL_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_bool_summary poldiff_bool_summary_t;
+
+/**
+ * Allocate and return a new poldiff_bool_summary_t object.
+ *
+ * @return A new bool summary. The caller must call bool_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_bool_summary_t *bool_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_bool_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param bs Reference to a bool summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void bool_destroy(poldiff_bool_summary_t ** bs);
+
+/**
+ * Reset the state of all boolean differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int bool_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all bools from the given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all bools. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *bool_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_bool_t objects, determining if they have the same
+ * name or not.
+ *
+ * @param x The bool from the original policy.
+ * @param y The bool from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if bool x is respectively less than, equal
+ * to, or greater than bool y.
+ */
+ int bool_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a bool.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int bool_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two bools for which the
+ * compare callback returns 0. If a difference is found then
+ * allocate, initialize, and insert an new semantic difference entry
+ * for that bool.
+ *
+ * @param diff The policy difference structure associated with both
+ * bools and to which to add an entry if needed.
+ * @param x The bool from the original policy.
+ * @param y The bool from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int bool_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_BOOL_INTERNAL_H */
diff --git a/libpoldiff/src/cat_diff.c b/libpoldiff/src/cat_diff.c
new file mode 100644
index 0000000..2f6ab17
--- /dev/null
+++ b/libpoldiff/src/cat_diff.c
@@ -0,0 +1,289 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in categories.
+ *
+ * @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 "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_cat_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_cat
+{
+ char *name;
+ poldiff_form_e form;
+};
+
+void poldiff_cat_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->cat_diffs->num_added;
+ stats[1] = diff->cat_diffs->num_removed;
+ stats[2] = 0;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+const apol_vector_t *poldiff_get_cat_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->cat_diffs->diffs;
+}
+
+char *poldiff_cat_to_string(const poldiff_t * diff, const void *cat)
+{
+ poldiff_cat_t *c = (poldiff_cat_t *) cat;
+ size_t len = 0;
+ char *s = NULL;
+ if (diff == NULL || cat == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (c->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", c->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", c->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+const char *poldiff_cat_get_name(const poldiff_cat_t * cat)
+{
+ if (cat == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return cat->name;
+}
+
+poldiff_form_e poldiff_cat_get_form(const void *cat)
+{
+ if (cat == NULL) {
+ errno = EINVAL;
+ return POLDIFF_FORM_NONE;
+ }
+
+ return ((const poldiff_cat_t *)cat)->form;
+}
+
+static void cat_free(void *elem)
+{
+ poldiff_cat_t *s = elem;
+ if (!elem)
+ return;
+ free(s->name);
+ free(s);
+}
+
+poldiff_cat_summary_t *cat_create(void)
+{
+ poldiff_cat_summary_t *cs = calloc(1, sizeof(poldiff_cat_summary_t));
+ if (cs == NULL)
+ return NULL;
+ if ((cs->diffs = apol_vector_create(cat_free)) == NULL) {
+ cat_destroy(&cs);
+ return NULL;
+ }
+ return cs;
+}
+
+void cat_destroy(poldiff_cat_summary_t ** cs)
+{
+ if (cs == NULL || *cs == NULL)
+ return;
+ apol_vector_destroy(&(*cs)->diffs);
+ free(*cs);
+ *cs = NULL;
+}
+
+int cat_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ cat_destroy(&diff->cat_diffs);
+ diff->cat_diffs = cat_create();
+ if (diff->cat_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two categories from the same policy.
+ */
+static int cat_name_comp(const void *x, const void *y, void *arg)
+{
+ const qpol_cat_t *c1 = x;
+ const qpol_cat_t *c2 = y;
+ apol_policy_t *p = arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+
+ if (qpol_cat_get_name(q, c1, &name1) < 0 || qpol_cat_get_name(q, c2, &name2) < 0)
+ return 0;
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *cat_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_cat_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create_from_iter(iter, NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, cat_name_comp, (void *)policy);
+ return v;
+}
+
+int cat_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ const qpol_cat_t *c1 = x;
+ const qpol_cat_t *c2 = y;
+ const char *name1, *name2;
+ if (qpol_cat_get_name(diff->orig_qpol, c1, &name1) < 0 || qpol_cat_get_name(diff->mod_qpol, c2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new category difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the category that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling cat_free() upon the returned
+ * value.
+ */
+static poldiff_cat_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_cat_t *pl;
+ int error;
+ if ((pl = calloc(1, sizeof(*pl))) == NULL || (pl->name = strdup(name)) == NULL) {
+ error = errno;
+ cat_free(pl);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pl->form = form;
+ return pl;
+}
+
+int cat_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const qpol_cat_t *c = item;
+ const char *name = NULL;
+ poldiff_cat_t *pl;
+ int error;
+ if ((form == POLDIFF_FORM_ADDED &&
+ qpol_cat_get_name(diff->mod_qpol, c, &name) < 0) ||
+ ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_cat_get_name(diff->orig_qpol, c, &name) < 0)) {
+ return -1;
+ }
+ pl = make_diff(diff, form, name);
+ if (pl == NULL) {
+ return -1;
+ }
+ if (apol_vector_append(diff->cat_diffs->diffs, pl) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ cat_free(pl);
+ errno = error;
+ return -1;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->cat_diffs->num_added++;
+ } else {
+ diff->cat_diffs->num_removed++;
+ }
+ return 0;
+}
+
+int cat_deep_diff(poldiff_t * diff __attribute__ ((unused)), const void *x __attribute__ ((unused)), const void *y
+ __attribute__ ((unused)))
+{
+ /* Categories cannot be modified only added or removed.
+ * This call back simply returns 0 to satisfy the generic diff algorithm. */
+ return 0;
+}
diff --git a/libpoldiff/src/cat_internal.h b/libpoldiff/src/cat_internal.h
new file mode 100644
index 0000000..4c5714e
--- /dev/null
+++ b/libpoldiff/src/cat_internal.h
@@ -0,0 +1,120 @@
+/**
+ * @file
+ * Protected interface for computing semantic differences in
+ * categories.
+ *
+ * @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 POLDIFF_CAT_INTERNAL_H
+#define POLDIFF_CAT_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_cat_summary poldiff_cat_summary_t;
+
+/**
+ * Allocate and return a new poldiff_cat_summary_t object.
+ *
+ * @return A new category summary. The caller must call cat_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_cat_summary_t *cat_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_cat_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param cs Reference to a category summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void cat_destroy(poldiff_cat_summary_t ** cs);
+
+/**
+ * Reset the state of all category differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int cat_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all categoriess from the given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all categories. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *cat_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_cat_t objects, determining if they have the same
+ * level name or not.
+ *
+ * @param x The category from the original policy.
+ * @param y The category from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if category x is respectively less than, equal
+ * to, or greater than category y.
+ */
+ int cat_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a category.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int cat_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two categories for which the compare
+ * callback returns 0. Categories cannot be modified only added or removed.
+ * This function always returns 0.
+ *
+ * @param diff The policy difference structure associated with both
+ * categories.
+ * @param x The category from the original policy.
+ * @param y The category from the modified policy.
+ *
+ * @return always 0.
+ */
+ int cat_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_CAT_INTERNAL_H */
diff --git a/libpoldiff/src/class_diff.c b/libpoldiff/src/class_diff.c
new file mode 100644
index 0000000..1c4541c
--- /dev/null
+++ b/libpoldiff/src/class_diff.c
@@ -0,0 +1,990 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in classes and
+ * commons.
+ *
+ * @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 "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/******************** object classes ********************/
+
+struct poldiff_class_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_class
+{
+ char *name;
+ poldiff_form_e form;
+ apol_vector_t *added_perms;
+ apol_vector_t *removed_perms;
+};
+
+void poldiff_class_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->class_diffs->num_added;
+ stats[1] = diff->class_diffs->num_removed;
+ stats[2] = diff->class_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+char *poldiff_class_to_string(const poldiff_t * diff, const void *cls)
+{
+ poldiff_class_t *c = (poldiff_class_t *) cls;
+ size_t num_added, num_removed, len = 0, i;
+ char *s = NULL, *perm;
+ if (diff == NULL || cls == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ num_added = apol_vector_get_size(c->added_perms);
+ num_removed = apol_vector_get_size(c->removed_perms);
+ switch (c->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", c->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", c->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "* %s (", c->name) < 0) {
+ s = NULL;
+ break;
+ }
+ if (num_added > 0) {
+ if (apol_str_appendf(&s, &len, "%zd Added Permission%s", num_added, (num_added == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (num_removed > 0) {
+ if (apol_str_appendf
+ (&s, &len, "%s%zd Removed Permission%s", (num_added > 0 ? ", " : ""), num_removed,
+ (num_removed == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (apol_str_append(&s, &len, ")\n") < 0) {
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(c->added_perms); i++) {
+ perm = (char *)apol_vector_get_element(c->added_perms, i);
+ if (apol_str_appendf(&s, &len, "\t+ %s\n", perm) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(c->removed_perms); i++) {
+ perm = (char *)apol_vector_get_element(c->removed_perms, i);
+ if (apol_str_appendf(&s, &len, "\t- %s\n", perm) < 0) {
+ goto err;
+ }
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ err:
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_class_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->class_diffs->diffs;
+}
+
+const char *poldiff_class_get_name(const poldiff_class_t * cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return cls->name;
+}
+
+poldiff_form_e poldiff_class_get_form(const void *cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_class_t *)cls)->form;
+}
+
+const apol_vector_t *poldiff_class_get_added_perms(const poldiff_class_t * cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return cls->added_perms;
+}
+
+const apol_vector_t *poldiff_class_get_removed_perms(const poldiff_class_t * cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return cls->removed_perms;
+}
+
+/*************** protected functions for object classes ***************/
+
+static void class_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_class_t *c = (poldiff_class_t *) elem;
+ free(c->name);
+ apol_vector_destroy(&c->added_perms);
+ apol_vector_destroy(&c->removed_perms);
+ free(c);
+ }
+}
+
+poldiff_class_summary_t *class_create(void)
+{
+ poldiff_class_summary_t *cs = calloc(1, sizeof(*cs));
+ if (cs == NULL) {
+ return NULL;
+ }
+ if ((cs->diffs = apol_vector_create(class_free)) == NULL) {
+ class_destroy(&cs);
+ return NULL;
+ }
+ return cs;
+}
+
+void class_destroy(poldiff_class_summary_t ** cs)
+{
+ if (cs != NULL && *cs != NULL) {
+ apol_vector_destroy(&(*cs)->diffs);
+ free(*cs);
+ *cs = NULL;
+ }
+}
+
+int class_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ class_destroy(&diff->class_diffs);
+ diff->class_diffs = class_create();
+ if (diff->class_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two classes from the same policy.
+ */
+static int class_name_comp(const void *x, const void *y, void *arg)
+{
+ const qpol_class_t *c1 = x;
+ const qpol_class_t *c2 = y;
+ apol_policy_t *p = (apol_policy_t *) arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+ if (qpol_class_get_name(q, c1, &name1) < 0 || qpol_class_get_name(q, c2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *class_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_class_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create_from_iter(iter, NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, class_name_comp, (void *)policy);
+ return v;
+}
+
+int class_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ const qpol_class_t *c1 = x;
+ const qpol_class_t *c2 = y;
+ const char *name1, *name2;
+ if (qpol_class_get_name(diff->orig_qpol, c1, &name1) < 0 || qpol_class_get_name(diff->mod_qpol, c2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new class difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the class that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling class_free() upon the
+ * returned value.
+ */
+static poldiff_class_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_class_t *pc;
+ int error;
+ if ((pc = calloc(1, sizeof(*pc))) == NULL ||
+ (pc->name = strdup(name)) == NULL ||
+ (pc->added_perms = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pc->removed_perms = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = errno;
+ class_free(pc);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pc->form = form;
+ return pc;
+}
+
+int class_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const qpol_class_t *c = item;
+ const char *name = NULL;
+ poldiff_class_t *pc;
+ int error;
+ if ((form == POLDIFF_FORM_ADDED &&
+ qpol_class_get_name(diff->mod_qpol, c, &name) < 0) ||
+ ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_class_get_name(diff->orig_qpol, c, &name) < 0))
+ {
+ return -1;
+ }
+ pc = make_diff(diff, form, name);
+ if (pc == NULL) {
+ return -1;
+ }
+ if (apol_vector_append(diff->class_diffs->diffs, pc) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ class_free(pc);
+ errno = error;
+ return -1;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->class_diffs->num_added++;
+ } else {
+ diff->class_diffs->num_removed++;
+ }
+ return 0;
+}
+
+/**
+ * Given an object class, return a vector of its permissions (in the
+ * form of strings). These permissions include those inherited from
+ * the class's common, if present.
+ *
+ * @param diff Policy diff error handler.
+ * @param p Policy from which the class came.
+ * @param class Class whose permissions to get.
+ *
+ * @return Vector of permissions strings for the class. The caller is
+ * responsible for calling apol_vector_destroy(). On error, return
+ * NULL.
+ */
+static apol_vector_t *class_get_perms(const poldiff_t * diff, const apol_policy_t * p, const qpol_class_t * class)
+{
+ const qpol_common_t *common;
+ qpol_iterator_t *perm_iter = NULL, *common_iter = NULL;
+ char *perm;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1;
+
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_class_get_common(q, class, &common) < 0 || qpol_class_get_perm_iter(q, class, &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) {
+ goto cleanup;
+ }
+ if (apol_vector_append(v, perm) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ if (common != NULL) {
+ if (qpol_common_get_perm_iter(q, common, &common_iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(common_iter); qpol_iterator_next(common_iter)) {
+ if (qpol_iterator_get_item(common_iter, (void **)&perm) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_append(v, perm) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&perm_iter);
+ qpol_iterator_destroy(&common_iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ return NULL;
+ }
+ return v;
+}
+
+int class_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const qpol_class_t *c1 = x;
+ const qpol_class_t *c2 = y;
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ char *perm1 = NULL, *perm2 = NULL;
+ const char *name;
+ poldiff_class_t *c = NULL;
+ size_t i, j;
+ int retval = -1, error = 0, compval;
+
+ if (qpol_class_get_name(diff->orig_qpol, c1, &name) < 0 ||
+ (v1 = class_get_perms(diff, diff->orig_pol, c1)) == NULL || (v2 = class_get_perms(diff, diff->mod_pol, c2)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_sort(v1, apol_str_strcmp, NULL);
+ apol_vector_sort(v2, apol_str_strcmp, NULL);
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2))
+ break;
+ perm1 = (char *)apol_vector_get_element(v1, i);
+ perm2 = (char *)apol_vector_get_element(v2, j);
+ compval = strcmp(perm1, perm2);
+ if (compval != 0 && c == NULL) {
+ if ((c = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if (compval < 0) {
+ if ((perm1 = strdup(perm1)) == NULL || apol_vector_append(c->removed_perms, perm1) < 0) {
+ error = errno;
+ free(perm1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (compval > 0) {
+ if ((perm2 = strdup(perm2)) == NULL || apol_vector_append(c->added_perms, perm2) < 0) {
+ error = errno;
+ free(perm2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ perm1 = (char *)apol_vector_get_element(v1, i);
+ if (c == NULL) {
+ if ((c = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if ((perm1 = strdup(perm1)) == NULL || apol_vector_append(c->removed_perms, perm1) < 0) {
+ error = errno;
+ free(perm1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ perm2 = (char *)apol_vector_get_element(v2, j);
+ if (c == NULL) {
+ if ((c = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if ((perm2 = strdup(perm2)) == NULL || apol_vector_append(c->added_perms, perm2) < 0) {
+ error = errno;
+ free(perm2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (c != NULL) {
+ apol_vector_sort(c->removed_perms, apol_str_strcmp, NULL);
+ apol_vector_sort(c->added_perms, apol_str_strcmp, NULL);
+ if (apol_vector_append(diff->class_diffs->diffs, c) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->class_diffs->num_modified++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ if (retval != 0) {
+ class_free(c);
+ }
+ errno = error;
+ return retval;
+}
+
+/******************** common classes ********************/
+
+struct poldiff_common_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_common
+{
+ char *name;
+ poldiff_form_e form;
+ apol_vector_t *added_perms;
+ apol_vector_t *removed_perms;
+};
+
+void poldiff_common_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->common_diffs->num_added;
+ stats[1] = diff->common_diffs->num_removed;
+ stats[2] = diff->common_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+char *poldiff_common_to_string(const poldiff_t * diff, const void *cls)
+{
+ poldiff_common_t *c = (poldiff_common_t *) cls;
+ size_t num_added, num_removed, len = 0, i;
+ char *s = NULL, *perm;
+ if (diff == NULL || cls == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ num_added = apol_vector_get_size(c->added_perms);
+ num_removed = apol_vector_get_size(c->removed_perms);
+ switch (c->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", c->name) < 0) {
+ s = NULL;
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", c->name) < 0) {
+ s = NULL;
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "* %s (", c->name) < 0) {
+ s = NULL;
+ break;
+ }
+ if (num_added > 0) {
+ if (apol_str_appendf(&s, &len, "%zd Added Permission%s", num_added, (num_added == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (num_removed > 0) {
+ if (apol_str_appendf
+ (&s, &len, "%s%zd Removed Permission%s", (num_added > 0 ? ", " : ""), num_removed,
+ (num_removed == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (apol_str_append(&s, &len, ")\n") < 0) {
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(c->added_perms); i++) {
+ perm = (char *)apol_vector_get_element(c->added_perms, i);
+ if (apol_str_appendf(&s, &len, "\t+ %s\n", perm) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(c->removed_perms); i++) {
+ perm = (char *)apol_vector_get_element(c->removed_perms, i);
+ if (apol_str_appendf(&s, &len, "\t- %s\n", perm) < 0) {
+ goto err;
+ }
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ err:
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_common_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->common_diffs->diffs;
+}
+
+const char *poldiff_common_get_name(const poldiff_common_t * cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return cls->name;
+}
+
+poldiff_form_e poldiff_common_get_form(const void *cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_common_t *)cls)->form;
+}
+
+const apol_vector_t *poldiff_common_get_added_perms(const poldiff_common_t * cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return cls->added_perms;
+}
+
+const apol_vector_t *poldiff_common_get_removed_perms(const poldiff_common_t * cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return cls->removed_perms;
+}
+
+/*************** protected functions for common classes ***************/
+
+static void common_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_common_t *c = (poldiff_common_t *) elem;
+ free(c->name);
+ apol_vector_destroy(&c->added_perms);
+ apol_vector_destroy(&c->removed_perms);
+ free(c);
+ }
+}
+
+poldiff_common_summary_t *common_create(void)
+{
+ poldiff_common_summary_t *cs = calloc(1, sizeof(*cs));
+ if (cs == NULL) {
+ return NULL;
+ }
+ if ((cs->diffs = apol_vector_create(common_free)) == NULL) {
+ common_destroy(&cs);
+ return NULL;
+ }
+ return cs;
+}
+
+void common_destroy(poldiff_common_summary_t ** cs)
+{
+ if (cs != NULL && *cs != NULL) {
+ apol_vector_destroy(&(*cs)->diffs);
+ free(*cs);
+ *cs = NULL;
+ }
+}
+
+int common_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ common_destroy(&diff->common_diffs);
+ diff->common_diffs = common_create();
+ if (diff->common_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two commons from the same policy.
+ */
+static int common_name_comp(const void *x, const void *y, void *arg)
+{
+ const qpol_common_t *c1 = x;
+ const qpol_common_t *c2 = y;
+ apol_policy_t *p = (apol_policy_t *) arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+ if (qpol_common_get_name(q, c1, &name1) < 0 || qpol_common_get_name(q, c2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *common_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_common_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create_from_iter(iter, NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, common_name_comp, (void *)policy);
+ return v;
+}
+
+int common_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ const qpol_common_t *c1 = x;
+ const qpol_common_t *c2 = y;
+ const char *name1, *name2;
+ if (qpol_common_get_name(diff->orig_qpol, c1, &name1) < 0 || qpol_common_get_name(diff->mod_qpol, c2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new common difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the common that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling common_free() upon the
+ * returned value.
+ */
+static poldiff_common_t *make_common_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_common_t *pc;
+ int error;
+ if ((pc = calloc(1, sizeof(*pc))) == NULL ||
+ (pc->name = strdup(name)) == NULL ||
+ (pc->added_perms = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pc->removed_perms = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = errno;
+ common_free(pc);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pc->form = form;
+ return pc;
+}
+
+int common_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const qpol_common_t *c = item;
+ const char *name = NULL;
+ poldiff_common_t *pc;
+ int error;
+ if ((form == POLDIFF_FORM_ADDED &&
+ qpol_common_get_name(diff->mod_qpol, c, &name) < 0) ||
+ ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) &&
+ qpol_common_get_name(diff->orig_qpol, c, &name) < 0)) {
+ return -1;
+ }
+ pc = make_common_diff(diff, form, name);
+ if (pc == NULL) {
+ return -1;
+ }
+ if (apol_vector_append(diff->common_diffs->diffs, pc) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ common_free(pc);
+ errno = error;
+ return -1;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->common_diffs->num_added++;
+ } else {
+ diff->common_diffs->num_removed++;
+ }
+ return 0;
+}
+
+/**
+ * Given a common class, return a vector of its permissions (in the
+ * form of strings).
+ *
+ * @param diff Policy diff error handler.
+ * @param p Policy from which the common came.
+ * @param common Common whose permissions to get.
+ *
+ * @return Vector of permissions strings for the common. The caller
+ * is responsible for calling apol_vector_destroy(). On error, return
+ * NULL.
+ */
+static apol_vector_t *common_get_perms(const poldiff_t * diff, const apol_policy_t * p, const qpol_common_t * common)
+{
+ qpol_iterator_t *perm_iter = NULL;
+ char *perm;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1;
+
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_common_get_perm_iter(q, common, &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) {
+ goto cleanup;
+ }
+ if (apol_vector_append(v, perm) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&perm_iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ return NULL;
+ }
+ return v;
+}
+
+int common_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const qpol_common_t *c1 = x;
+ const qpol_common_t *c2 = y;
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ char *perm1 = NULL, *perm2 = NULL;
+ const char *name;
+ poldiff_common_t *c = NULL;
+ size_t i, j;
+ int retval = -1, error = 0, compval;
+
+ if (qpol_common_get_name(diff->orig_qpol, c1, &name) < 0 ||
+ (v1 = common_get_perms(diff, diff->orig_pol, c1)) == NULL || (v2 = common_get_perms(diff, diff->mod_pol, c2)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_sort(v1, apol_str_strcmp, NULL);
+ apol_vector_sort(v2, apol_str_strcmp, NULL);
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2))
+ break;
+ perm1 = (char *)apol_vector_get_element(v1, i);
+ perm2 = (char *)apol_vector_get_element(v2, j);
+ compval = strcmp(perm1, perm2);
+ if (compval != 0 && c == NULL) {
+ if ((c = make_common_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if (compval < 0) {
+ if ((perm1 = strdup(perm1)) == NULL || apol_vector_append(c->removed_perms, perm1) < 0) {
+ error = errno;
+ free(perm1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (compval > 0) {
+ if ((perm2 = strdup(perm2)) == NULL || apol_vector_append(c->added_perms, perm2) < 0) {
+ error = errno;
+ free(perm2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ perm1 = (char *)apol_vector_get_element(v1, i);
+ if (c == NULL) {
+ if ((c = make_common_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if ((perm1 = strdup(perm1)) == NULL || apol_vector_append(c->removed_perms, perm1) < 0) {
+ error = errno;
+ free(perm1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ perm2 = (char *)apol_vector_get_element(v2, j);
+ if (c == NULL) {
+ if ((c = make_common_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if ((perm2 = strdup(perm2)) == NULL || apol_vector_append(c->added_perms, perm2) < 0) {
+ error = errno;
+ free(perm2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (c != NULL) {
+ apol_vector_sort(c->removed_perms, apol_str_strcmp, NULL);
+ apol_vector_sort(c->added_perms, apol_str_strcmp, NULL);
+ if (apol_vector_append(diff->common_diffs->diffs, c) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->common_diffs->num_modified++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ if (retval != 0) {
+ common_free(c);
+ }
+ errno = error;
+ return retval;
+}
diff --git a/libpoldiff/src/class_internal.h b/libpoldiff/src/class_internal.h
new file mode 100644
index 0000000..69f6fc0
--- /dev/null
+++ b/libpoldiff/src/class_internal.h
@@ -0,0 +1,212 @@
+/**
+ * @file
+ * Protected interface for class and common differences.
+ *
+ * @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 POLDIFF_CLASS_INTERNAL_H
+#define POLDIFF_CLASS_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/******************** object classes ********************/
+
+ typedef struct poldiff_class_summary poldiff_class_summary_t;
+
+/**
+ * Allocate and return a new poldiff_class_summary_t object.
+ *
+ * @return A new class summary. The caller must call class_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_class_summary_t *class_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_class_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param cs Reference to a class summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void class_destroy(poldiff_class_summary_t ** cs);
+
+/**
+ * Reset the state of all class differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int class_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all object classes (type qpol_class_t) from the
+ * given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all classes. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *class_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_class_t objects, determining if they have the same
+ * name or not.
+ *
+ * @param x The class from the original policy.
+ * @param y The class from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if class x is respectively less than, equal
+ * to, or greater than class y.
+ */
+ int class_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a class.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int class_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two classes for which the
+ * compare callback returns 0. If a difference is found then
+ * allocate, initialize, and insert a new semantic difference entry
+ * for that class.
+ *
+ * @param diff The policy difference structure associated with both
+ * classes and to which to add an entry if needed.
+ * @param x The class from the original policy.
+ * @param y The class from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int class_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+/******************** common classes ********************/
+
+ typedef struct poldiff_common_summary poldiff_common_summary_t;
+
+/**
+ * Allocate and return a new poldiff_common_summary_t object.
+ *
+ * @return A new common summary. The caller must call
+ * common_destroy() afterwards. On error, return NULL and set errno.
+ */
+ poldiff_common_summary_t *common_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_common_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param cs Reference to a common summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void common_destroy(poldiff_common_summary_t ** cs);
+
+/**
+ * Reset the state of all common differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int common_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all common classes (type qpol_common_t) from the
+ * given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all commons. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *common_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_common_t objects, determining if they have the
+ * same name or not.
+ *
+ * @param x The common from the original policy.
+ * @param y The common from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if common x is respectively less than, equal
+ * to, or greater than common y.
+ */
+ int common_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a common.
+ *
+ * @param diff The policy difference structure to which to add the
+ * entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int common_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two commons for which the
+ * compare callback returns 0. If a difference is found then
+ * allocate, initialize, and insert a new semantic difference entry
+ * for that common.
+ *
+ * @param diff The policy difference structure associated with both
+ * commons and to which to add an entry if needed.
+ * @param x The common from the original policy.
+ * @param y The common from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int common_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_CLASS_INTERNAL_H */
diff --git a/libpoldiff/src/level_diff.c b/libpoldiff/src/level_diff.c
new file mode 100644
index 0000000..b384519
--- /dev/null
+++ b/libpoldiff/src/level_diff.c
@@ -0,0 +1,769 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in levels.
+ *
+ * @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 "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_level_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+void poldiff_level_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->level_diffs->num_added;
+ stats[1] = diff->level_diffs->num_removed;
+ stats[2] = diff->level_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+const apol_vector_t *poldiff_get_level_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->level_diffs->diffs;
+}
+
+char *poldiff_level_to_string(const poldiff_t * diff, const void *level)
+{
+ poldiff_level_t *l = (poldiff_level_t *) level;
+ size_t num_added, num_removed, len = 0, i;
+ char *s = NULL, *cat;
+ if (diff == NULL || level == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ num_added = apol_vector_get_size(l->added_cats);
+ num_removed = apol_vector_get_size(l->removed_cats);
+ switch (l->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", l->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", l->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "* %s (", l->name) < 0) {
+ break;
+ }
+ if (num_added > 0) {
+ if (apol_str_appendf(&s, &len, "%zd Added %s", num_added, (num_added == 1 ? "Category" : "Categories")) < 0) {
+ break;
+ }
+ }
+ if (num_removed > 0) {
+ if (apol_str_appendf
+ (&s, &len, "%s%zd Removed %s", (num_added > 0 ? ", " : ""), num_removed,
+ (num_removed == 1 ? "Category" : "Categories")) < 0) {
+ break;
+ }
+ }
+ if (apol_str_append(&s, &len, ")\n") < 0) {
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(l->added_cats); i++) {
+ cat = (char *)apol_vector_get_element(l->added_cats, i);
+ if (apol_str_appendf(&s, &len, "\t+ %s\n", cat) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(l->removed_cats); i++) {
+ cat = (char *)apol_vector_get_element(l->removed_cats, i);
+ if (apol_str_appendf(&s, &len, "\t- %s\n", cat) < 0) {
+ goto err;
+ }
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ err:
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+char *poldiff_level_to_string_brief(const poldiff_t * diff, const poldiff_level_t * level)
+{
+ char *s = NULL, t, *cat, *sep = "";
+ int show_cat_sym = 0;
+ size_t len = 0, i;
+ switch (level->form) {
+ case POLDIFF_FORM_ADDED:
+ t = '+';
+ break;
+ case POLDIFF_FORM_REMOVED:
+ t = '-';
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ t = '*';
+ show_cat_sym = 1;
+ break;
+ default:
+ /* don't show unmodified levels */
+ if ((s = strdup("")) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ return s;
+ }
+ if (apol_str_appendf(&s, &len, "%c %s", t, level->name) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ if ((level->unmodified_cats != NULL && apol_vector_get_size(level->unmodified_cats) > 0) ||
+ (level->added_cats != NULL && apol_vector_get_size(level->added_cats) > 0) ||
+ (level->removed_cats != NULL && apol_vector_get_size(level->removed_cats) > 0)) {
+ if (apol_str_append(&s, &len, " : ") < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ for (i = 0; level->unmodified_cats != NULL && i < apol_vector_get_size(level->unmodified_cats); i++) {
+ cat = apol_vector_get_element(level->unmodified_cats, i);
+ if (apol_str_appendf(&s, &len, "%s%s", sep, cat) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ sep = ",";
+ }
+ for (i = 0; level->added_cats != NULL && i < apol_vector_get_size(level->added_cats); i++) {
+ cat = apol_vector_get_element(level->added_cats, i);
+ if (apol_str_appendf(&s, &len, "%s%s%s", sep, (show_cat_sym ? "+" : ""), cat) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ sep = ",";
+ }
+ for (i = 0; level->removed_cats != NULL && i < apol_vector_get_size(level->removed_cats); i++) {
+ cat = apol_vector_get_element(level->removed_cats, i);
+ if (apol_str_appendf(&s, &len, "%s%s%s", sep, (show_cat_sym ? "-" : ""), cat) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ sep = ",";
+ }
+ }
+ if (apol_str_append(&s, &len, "\n") < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ return s;
+}
+
+const char *poldiff_level_get_name(const poldiff_level_t * level)
+{
+ if (level == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return level->name;
+}
+
+poldiff_form_e poldiff_level_get_form(const void *level)
+{
+ if (level == NULL) {
+ errno = EINVAL;
+ return POLDIFF_FORM_NONE;
+ }
+
+ return ((const poldiff_level_t *)level)->form;
+}
+
+const apol_vector_t *poldiff_level_get_added_cats(const poldiff_level_t * level)
+{
+ if (level == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return level->added_cats;
+}
+
+const apol_vector_t *poldiff_level_get_removed_cats(const poldiff_level_t * level)
+{
+ if (level == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return level->removed_cats;
+}
+
+const apol_vector_t *poldiff_level_get_unmodified_cats(const poldiff_level_t * level)
+{
+ if (level == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return level->unmodified_cats;
+}
+
+poldiff_level_summary_t *level_create(void)
+{
+ poldiff_level_summary_t *ls = calloc(1, sizeof(poldiff_level_summary_t));
+ if (ls == NULL)
+ return NULL;
+ if ((ls->diffs = apol_vector_create(level_free)) == NULL) {
+ level_destroy(&ls);
+ return NULL;
+ }
+ return ls;
+}
+
+void level_destroy(poldiff_level_summary_t ** ls)
+{
+ if (ls == NULL || *ls == NULL)
+ return;
+ apol_vector_destroy(&(*ls)->diffs);
+ free(*ls);
+ *ls = NULL;
+}
+
+int level_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ level_destroy(&diff->level_diffs);
+ diff->level_diffs = level_create();
+ if (diff->level_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two levels from the same policy.
+ */
+static int level_name_comp(const void *x, const void *y, void *arg)
+{
+ const qpol_level_t *s1 = x;
+ const qpol_level_t *s2 = y;
+ apol_policy_t *p = arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+
+ if (qpol_level_get_name(q, s1, &name1) < 0 || qpol_level_get_name(q, s2, &name2) < 0)
+ return 0;
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *level_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_level_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create_from_iter(iter, NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, level_name_comp, (void *)policy);
+ return v;
+}
+
+int level_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ const qpol_level_t *l1 = x;
+ const qpol_level_t *l2 = y;
+ const char *name1, *name2;
+ if (qpol_level_get_name(diff->orig_qpol, l1, &name1) < 0 || qpol_level_get_name(diff->mod_qpol, l2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new level difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the level that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling level_free() upon the returned
+ * value.
+ */
+static poldiff_level_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_level_t *pl;
+ int error;
+ if ((pl = calloc(1, sizeof(*pl))) == NULL || (pl->name = strdup(name)) == NULL ||
+ (pl->added_cats = apol_vector_create(free)) == NULL ||
+ (pl->removed_cats = apol_vector_create(free)) == NULL || (pl->unmodified_cats = apol_vector_create(free)) == NULL) {
+ error = errno;
+ level_free(pl);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pl->form = form;
+ return pl;
+}
+
+/**
+ * Given a level, return a vector of its allowed categories (in the
+ * form of strings). These will be sorted in policy order.
+ *
+ * @param diff Policy diff error handler.
+ * @param p Policy from which the level came.
+ * @param level Level whose categories to get.
+ *
+ * @return Vector of category strings for the level. The caller is
+ * responsible for calling apol_vector_destroy(). On error, return
+ * NULL.
+ */
+static apol_vector_t *level_get_cats(const poldiff_t * diff, const apol_policy_t * p, const qpol_level_t * level)
+{
+ qpol_iterator_t *iter = NULL;
+ const qpol_cat_t *cat;
+ const char *cat_name;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1, error = 0;
+
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_level_get_cat_iter(q, level, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&cat) < 0 || qpol_cat_get_name(q, cat, &cat_name)) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_append(v, (void *)cat_name) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+int level_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const qpol_level_t *l = item;
+ const char *name = NULL;
+ poldiff_level_t *pl = NULL;
+ apol_policy_t *p;
+ qpol_policy_t *q;
+ apol_vector_t *v = NULL;
+ int error = 0, retval = -1;
+ if (form == POLDIFF_FORM_ADDED) {
+ p = diff->mod_pol;
+ q = diff->mod_qpol;
+ } else {
+ p = diff->orig_pol;
+ q = diff->orig_qpol;
+ }
+ if (qpol_level_get_name(q, l, &name) < 0 || (pl = make_diff(diff, form, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((v = level_get_cats(diff, p, l)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ apol_vector_destroy(&pl->added_cats);
+ if ((pl->added_cats = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ } else if (form == POLDIFF_FORM_REMOVED) {
+ apol_vector_destroy(&pl->removed_cats);
+ if ((pl->removed_cats = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_append(diff->level_diffs->diffs, pl) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->level_diffs->num_added++;
+ } else {
+ diff->level_diffs->num_removed++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v);
+ if (retval < 0) {
+ level_free(pl);
+ errno = error;
+ }
+ return retval;
+}
+
+/**
+ * Comparison function for two category names from the same policy.
+ *
+ * @param a Name of a category.
+ * @param b Name of another category.
+ * @param data qpol policy from which the categories originate.
+ *
+ * @return Less than zero, zero, or greater than zero based upon the
+ * categories' order within the policy.
+ */
+static int level_cat_comp(const void *a, const void *b, void *data)
+{
+ const char *name1 = (const char *)a;
+ const char *name2 = (const char *)b;
+ qpol_policy_t *q = (qpol_policy_t *) data;
+ const qpol_cat_t *cat1, *cat2;
+ qpol_policy_get_cat_by_name(q, name1, &cat1);
+ qpol_policy_get_cat_by_name(q, name2, &cat2);
+ assert(cat1 != NULL && cat2 != NULL);
+ uint32_t val1, val2;
+ qpol_cat_get_value(q, cat1, &val1);
+ qpol_cat_get_value(q, cat2, &val2);
+ return val1 - val2;
+}
+
+int level_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const qpol_level_t *l1 = x;
+ const qpol_level_t *l2 = y;
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ apol_vector_t *added = NULL, *removed = NULL, *unmodified = NULL;
+ const char *name;
+ poldiff_level_t *l = NULL;
+ int retval = -1, error = 0, compval;
+
+ if (qpol_level_get_name(diff->orig_qpol, l1, &name) < 0 ||
+ (v1 = level_get_cats(diff, diff->orig_pol, l1)) == NULL || (v2 = level_get_cats(diff, diff->mod_pol, l2)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_sort(v1, apol_str_strcmp, NULL);
+ apol_vector_sort(v2, apol_str_strcmp, NULL);
+ compval = level_deep_diff_cats(diff, v1, v2, &added, &removed, &unmodified);
+ if (compval < 0) {
+ error = errno;
+ goto cleanup;
+ } else if (compval > 0) {
+ if ((l = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_destroy(&l->added_cats);
+ apol_vector_destroy(&l->removed_cats);
+ apol_vector_destroy(&l->unmodified_cats);
+ if ((l->added_cats = apol_vector_create_from_vector(added, apol_str_strdup, NULL, free)) == NULL ||
+ (l->removed_cats = apol_vector_create_from_vector(removed, apol_str_strdup, NULL, free)) == NULL ||
+ (l->unmodified_cats = apol_vector_create_from_vector(unmodified, apol_str_strdup, NULL, free)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ apol_vector_sort(l->removed_cats, level_cat_comp, diff->orig_qpol);
+ apol_vector_sort(l->added_cats, level_cat_comp, diff->mod_qpol);
+ apol_vector_sort(l->unmodified_cats, level_cat_comp, diff->orig_qpol);
+ if (apol_vector_append(diff->level_diffs->diffs, l) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->level_diffs->num_modified++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ apol_vector_destroy(&added);
+ apol_vector_destroy(&removed);
+ apol_vector_destroy(&unmodified);
+ if (retval != 0) {
+ level_free(l);
+ }
+ errno = error;
+ return retval;
+}
+
+poldiff_level_t *level_create_from_apol_mls_level(const apol_mls_level_t * level, poldiff_form_e form)
+{
+ const char *sens = apol_mls_level_get_sens(level);
+ const apol_vector_t *cats = apol_mls_level_get_cats(level);
+ poldiff_level_t *pl = NULL;
+ apol_vector_t **target;
+ if ((pl = calloc(1, sizeof(*pl))) == NULL ||
+ (pl->name = strdup(sens)) == NULL || (pl->unmodified_cats = apol_vector_create_with_capacity(1, free)) == NULL) {
+ level_free(pl);
+ return NULL;;
+ }
+ pl->form = form;
+ if (form == POLDIFF_FORM_ADDED) {
+ if ((pl->removed_cats = apol_vector_create_with_capacity(1, free)) == NULL) {
+ level_free(pl);
+ return NULL;
+ }
+ target = &pl->added_cats;
+ } else if (form == POLDIFF_FORM_REMOVED) {
+ if ((pl->added_cats = apol_vector_create_with_capacity(1, free)) == NULL) {
+ level_free(pl);
+ return NULL;
+ }
+ target = &pl->removed_cats;
+ } else {
+ if ((pl->added_cats = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pl->removed_cats = apol_vector_create_with_capacity(1, free)) == NULL) {
+ level_free(pl);
+ return NULL;
+ }
+ return pl;
+ }
+ if ((*target = apol_vector_create_from_vector(cats, apol_str_strdup, NULL, free)) == NULL) {
+ level_free(pl);
+ return NULL;
+ }
+ return pl;
+}
+
+void level_free(void *elem)
+{
+ poldiff_level_t *s = elem;
+ if (!elem)
+ return;
+ free(s->name);
+ apol_vector_destroy(&s->added_cats);
+ apol_vector_destroy(&s->removed_cats);
+ apol_vector_destroy(&s->unmodified_cats);
+ free(s);
+}
+
+int level_deep_diff_apol_mls_levels(poldiff_t * diff, const apol_mls_level_t * level1, const apol_mls_level_t * level2,
+ poldiff_level_t ** orig_pl, poldiff_level_t ** mod_pl)
+{
+ poldiff_level_t *u1 = NULL, *u2 = NULL;
+ apol_vector_t *added = NULL, *removed = NULL, *unmodified = NULL;
+ const char *sens1 = apol_mls_level_get_sens(level1);
+ const apol_vector_t *cats1 = apol_mls_level_get_cats(level1);
+ const char *sens2 = apol_mls_level_get_sens(level2);
+ const apol_vector_t *cats2 = apol_mls_level_get_cats(level2);
+ int retval = -1, compval;
+
+ *orig_pl = *mod_pl = NULL;
+ if (strcmp(sens1, sens2) != 0) {
+ /* sensitivities do not match, so don't check categories */
+ if ((u1 = make_diff(diff, POLDIFF_FORM_REMOVED, sens1)) == NULL ||
+ (u2 = make_diff(diff, POLDIFF_FORM_ADDED, sens2)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ level_free(u1);
+ level_free(u2);
+ return -1;
+ }
+ apol_vector_destroy(&u1->removed_cats);
+ apol_vector_destroy(&u2->added_cats);
+ if ((u1->removed_cats = apol_vector_create_from_vector(cats1, apol_str_strdup, NULL, free)) == NULL ||
+ (u2->added_cats = apol_vector_create_from_vector(cats2, apol_str_strdup, NULL, free)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ level_free(u1);
+ level_free(u2);
+ return -1;
+ }
+ apol_vector_sort(u1->removed_cats, level_cat_comp, diff->orig_qpol);
+ apol_vector_sort(u2->added_cats, level_cat_comp, diff->mod_qpol);
+ *orig_pl = u1;
+ *mod_pl = u2;
+ return 0;
+ }
+
+ compval = level_deep_diff_cats(diff, cats1, cats2, &added, &removed, &unmodified);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval > 0) {
+ if ((u1 = calloc(1, sizeof(*u1))) == NULL || (u1->name = strdup(sens1)) == NULL ||
+ (u1->added_cats = apol_vector_create_from_vector(added, apol_str_strdup, NULL, free)) == NULL ||
+ (u1->removed_cats = apol_vector_create_from_vector(removed, apol_str_strdup, NULL, free)) == NULL ||
+ (u1->unmodified_cats = apol_vector_create_from_vector(unmodified, apol_str_strdup, NULL, free)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ level_free(u1);
+ goto cleanup;
+ }
+ apol_vector_sort(u1->added_cats, level_cat_comp, diff->mod_qpol);
+ apol_vector_sort(u1->removed_cats, level_cat_comp, diff->orig_qpol);
+ apol_vector_sort(u1->unmodified_cats, level_cat_comp, diff->orig_qpol);
+ u1->form = POLDIFF_FORM_MODIFIED;
+ *orig_pl = u1;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&added);
+ apol_vector_destroy(&removed);
+ apol_vector_destroy(&unmodified);
+ return retval;
+}
+
+int level_deep_diff_cats(poldiff_t * diff, const apol_vector_t * v1, const apol_vector_t * v2, apol_vector_t ** added,
+ apol_vector_t ** removed, apol_vector_t ** unmodified)
+{
+ size_t i, j;
+ char *cat1, *cat2;
+ int compval, retval = -1, error = 0;
+ *added = *removed = *unmodified = NULL;
+ if ((*added = apol_vector_create(free)) == NULL ||
+ (*removed = apol_vector_create(free)) == NULL || (*unmodified = apol_vector_create(free)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2)) {
+ break;
+ }
+ cat1 = (char *)apol_vector_get_element(v1, i);
+ cat2 = (char *)apol_vector_get_element(v2, j);
+ compval = strcmp(cat1, cat2);
+ if (compval < 0) {
+ if ((cat1 = strdup(cat1)) == NULL || apol_vector_append(*removed, cat1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ free(cat1);
+ goto cleanup;
+ }
+ i++;
+ } else if (compval > 0) {
+ if ((cat2 = strdup(cat2)) == NULL || apol_vector_append(*added, cat2) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ free(cat2);
+ goto cleanup;
+ }
+ j++;
+ } else {
+ if ((cat1 = strdup(cat1)) == NULL || apol_vector_append(*unmodified, cat1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ free(cat1);
+ goto cleanup;
+ }
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ cat1 = (char *)apol_vector_get_element(v1, i);
+ if ((cat1 = strdup(cat1)) == NULL || apol_vector_append(*removed, cat1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ free(cat1);
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ cat2 = (char *)apol_vector_get_element(v2, j);
+ if ((cat2 = strdup(cat2)) == NULL || apol_vector_append(*added, cat2) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ free(cat2);
+ goto cleanup;
+ }
+ }
+ if (apol_vector_get_size(*added) > 0 || apol_vector_get_size(*removed) > 0) {
+ retval = 1;
+ } else {
+ retval = 0;
+ }
+ cleanup:
+ if (retval <= 0) {
+ /* if no differences found, then destroy all vectors */
+ apol_vector_destroy(added);
+ apol_vector_destroy(removed);
+ apol_vector_destroy(unmodified);
+ }
+ if (retval < 0) {
+ error = errno;
+ }
+ return retval;
+}
diff --git a/libpoldiff/src/level_internal.h b/libpoldiff/src/level_internal.h
new file mode 100644
index 0000000..4a0f603
--- /dev/null
+++ b/libpoldiff/src/level_internal.h
@@ -0,0 +1,208 @@
+/**
+ * @file
+ * Protected interface for computing semantic differences in levels,
+ * either from level declarations, user's default level, user's
+ * permitted range, or a range_transition's target range.
+ *
+ * @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 POLDIFF_LEVEL_INTERNAL_H
+#define POLDIFF_LEVEL_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_level_summary poldiff_level_summary_t;
+
+ struct poldiff_level
+ {
+ char *name;
+ poldiff_form_e form;
+ apol_vector_t *added_cats;
+ apol_vector_t *removed_cats;
+ apol_vector_t *unmodified_cats;
+ };
+
+/**
+ * Allocate and return a new poldiff_level_summary_t object.
+ *
+ * @return A new level summary. The caller must call level_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_level_summary_t *level_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_level_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param ls Reference to a level summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void level_destroy(poldiff_level_summary_t ** ls);
+
+/**
+ * Reset the state of all level differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int level_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all levels from the given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all levels. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *level_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_level_t objects, determining if they have the same
+ * level name or not.
+ *
+ * @param x The level from the original policy.
+ * @param y The level from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if level x is respectively less than, equal
+ * to, or greater than level y.
+ */
+ int level_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a level.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int level_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two levels for which the compare
+ * callback returns 0. If a difference is found then allocate,
+ * initialize, and insert a new semantic difference entry for that
+ * level.
+ *
+ * @param diff The policy difference structure associated with both
+ * levels and to which to add an entry if needed.
+ * @param x The level from the original policy.
+ * @param y The level from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int level_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+/*********************
+ * The remainder are protected functions that operate upon a single
+ * poldiff_level_t. These are used by user's default level, user's
+ * assigned range, etc.
+ *********************/
+
+/**
+ * Allocate and return a poldiff_level_t object. If the form is added
+ * or removed, set that respective vector to be all of the categories
+ * from the given level.
+ *
+ * @param level Level object to use as a template.
+ * @param form Form of difference for the level.
+ *
+ * @return Initialized level, or NULL upon error. Caller must call
+ * level_free() upon the returned value.
+ */
+ poldiff_level_t *level_create_from_apol_mls_level(const apol_mls_level_t * level, poldiff_form_e form);
+
+/**
+ * Deallocate all space associated with a poldiff_level_t, including
+ * the pointer itself.
+ *
+ * @param elem Pointer to a poldiff_level_t object. If NULL then do
+ * nothing.
+ */
+ void level_free(void *elem);
+
+/**
+ * Perform a deep diff of two levels. This will first compare the
+ * sensitivity names; if they match then it compares the vectors of
+ * category names. If the sensitivities do not match, then generate
+ * two poldiff_level_ts, one for the original level and one for
+ * modified level. If they do match then create just one
+ * poldiff_level_t and write it to orig_pl.
+ *
+ * @param diff Poldiff object, used for error reporting and for
+ * sorting the categories to policy order.
+ * @param level1 Original level.
+ * @param level2 Modified level.
+ * @param orig_pl Destination to where to write the poldiff_level_t,
+ * if the sensitivites do not match or if the categories do not match.
+ * @param mod_pl Destination to where to write the poldiff_level_t,
+ * if the sensitivities do not match.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ int level_deep_diff_apol_mls_levels(poldiff_t * diff, const apol_mls_level_t * level1, const apol_mls_level_t * level2,
+ poldiff_level_t ** orig_pl, poldiff_level_t ** mod_pl);
+
+/**
+ * Calculate the differences between two sorted vectors of category
+ * names. Allocate the vectors added, removed, and unmodified; fill
+ * them with appropriate category names. The returned vectors'
+ * categories will be sorted alphabetically.
+ *
+ * @param diff Error handler.
+ * @param v1 First vector of category names, sorted alphabetically.
+ * @param v2 Other vector of category names, sorted alphabetically.
+ * @param added Reference to where to store added categories. The
+ * caller is responsible for calling apol_vector_destroy() upon the
+ * value. If no differences are found then this will be set to NULL.
+ * @param removed Reference to where to store removed categories. The
+ * caller is responsible for calling apol_vector_destroy() upon the
+ * value. If no differences are found then this will be set to NULL.
+ * @param unmodified Reference to where to store unmodified
+ * categories. The caller is responsible for calling
+ * apol_vector_destroy() upon the value. If no differences are found
+ * then this will be set to NULL.
+ *
+ * @return Greater than zero if a difference was found, zero upon no
+ * differences, less than zero on error.
+ */
+ int level_deep_diff_cats(poldiff_t * diff, const apol_vector_t * v1, const apol_vector_t * v2, apol_vector_t ** added,
+ apol_vector_t ** removed, apol_vector_t ** unmodified);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_LEVEL_INTERNAL_H */
diff --git a/libpoldiff/src/libpoldiff.map b/libpoldiff/src/libpoldiff.map
new file mode 100644
index 0000000..29950c0
--- /dev/null
+++ b/libpoldiff/src/libpoldiff.map
@@ -0,0 +1,50 @@
+VERS_1.2{
+ global:
+ poldiff_create;
+ poldiff_destroy;
+ poldiff_run;
+ poldiff_is_run;
+ poldiff_type_remap_*;
+ poldiff_get_*;
+ poldiff_attrib_*;
+ poldiff_avrule_*;
+ poldiff_bool_*;
+ poldiff_cat_*;
+ poldiff_class_*;
+ poldiff_common_*;
+ poldiff_level_*;
+ poldiff_range_*;
+ poldiff_range_trans_*;
+ poldiff_role_*;
+ poldiff_role_allow_*;
+ poldiff_role_trans_*;
+ poldiff_terule_*;
+ poldiff_type_*;
+ poldiff_user_*;
+ libpoldiff_get_version;
+ poldiff_enable_line_numbers;
+ local: *;
+};
+
+VERS_1.3{
+ global:
+ poldiff_avrule_get_stats_allow;
+ poldiff_avrule_get_stats_auditallow;
+ poldiff_avrule_get_stats_dontaudit;
+ poldiff_avrule_get_stats_neverallow;
+ poldiff_get_avrule_vector_allow;
+ poldiff_get_avrule_vector_auditallow;
+ poldiff_get_avrule_vector_dontaudit;
+ poldiff_get_avrule_vector_neverallow;
+ poldiff_component_record_*;
+ poldiff_range_get_min_added_cats;
+ poldiff_range_get_min_removed_cats;
+ poldiff_range_get_min_unmodified_cats;
+ poldiff_role_allow_get_unmodified_roles;
+ poldiff_terule_get_stats_change;
+ poldiff_terule_get_stats_member;
+ poldiff_terule_get_stats_trans;
+ poldiff_get_terule_vector_change;
+ poldiff_get_terule_vector_member;
+ poldiff_get_terule_vector_trans;
+} VERS_1.2;
diff --git a/libpoldiff/src/poldiff.c b/libpoldiff/src/poldiff.c
new file mode 100644
index 0000000..cce4fc4
--- /dev/null
+++ b/libpoldiff/src/poldiff.c
@@ -0,0 +1,814 @@
+/**
+ * @file
+ * Implementation for computing a semantic policy difference.
+ *
+ * @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 "poldiff_internal.h"
+#include <poldiff/component_record.h>
+
+#include <apol/util.h>
+#include <qpol/policy_extend.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * All policy items (object classes, types, rules, etc.) must
+ * implement at least these functions. Next, a record should be
+ * appended to the array 'component_records' below.
+ */
+struct poldiff_component_record
+{
+ const char *item_name;
+ uint32_t flag_bit;
+ poldiff_get_item_stats_fn_t get_stats;
+ poldiff_get_result_items_fn_t get_results;
+ poldiff_item_get_form_fn_t get_form;
+ poldiff_item_to_string_fn_t to_string;
+ poldiff_reset_fn_t reset;
+ poldiff_get_items_fn_t get_items;
+ poldiff_item_comp_fn_t comp;
+ poldiff_new_diff_fn_t new_diff;
+ poldiff_deep_diff_fn_t deep_diff;
+};
+
+static const poldiff_component_record_t component_records[] = {
+ {
+ "attribute",
+ POLDIFF_DIFF_ATTRIBS,
+ poldiff_attrib_get_stats,
+ poldiff_get_attrib_vector,
+ poldiff_attrib_get_form,
+ poldiff_attrib_to_string,
+ attrib_reset,
+ attrib_get_items,
+ attrib_comp,
+ attrib_new_diff,
+ attrib_deep_diff,
+ },
+ {
+ "Allow Rules",
+ POLDIFF_DIFF_AVALLOW,
+ poldiff_avrule_get_stats_allow,
+ poldiff_get_avrule_vector_allow,
+ poldiff_avrule_get_form,
+ poldiff_avrule_to_string,
+ avrule_reset_allow,
+ avrule_get_items_allow,
+ avrule_comp,
+ avrule_new_diff_allow,
+ avrule_deep_diff_allow,
+ },
+ {
+ "Audit Allow Rules",
+ POLDIFF_DIFF_AVAUDITALLOW,
+ poldiff_avrule_get_stats_auditallow,
+ poldiff_get_avrule_vector_auditallow,
+ poldiff_avrule_get_form,
+ poldiff_avrule_to_string,
+ avrule_reset_auditallow,
+ avrule_get_items_auditallow,
+ avrule_comp,
+ avrule_new_diff_auditallow,
+ avrule_deep_diff_auditallow,
+ },
+ {
+ "Don't Audit Rules",
+ POLDIFF_DIFF_AVDONTAUDIT,
+ poldiff_avrule_get_stats_dontaudit,
+ poldiff_get_avrule_vector_dontaudit,
+ poldiff_avrule_get_form,
+ poldiff_avrule_to_string,
+ avrule_reset_dontaudit,
+ avrule_get_items_dontaudit,
+ avrule_comp,
+ avrule_new_diff_dontaudit,
+ avrule_deep_diff_dontaudit,
+ },
+ {
+ "Never Allow Rules",
+ POLDIFF_DIFF_AVNEVERALLOW,
+ poldiff_avrule_get_stats_neverallow,
+ poldiff_get_avrule_vector_neverallow,
+ poldiff_avrule_get_form,
+ poldiff_avrule_to_string,
+ avrule_reset_neverallow,
+ avrule_get_items_neverallow,
+ avrule_comp,
+ avrule_new_diff_neverallow,
+ avrule_deep_diff_neverallow,
+ },
+ {
+ "bool",
+ POLDIFF_DIFF_BOOLS,
+ poldiff_bool_get_stats,
+ poldiff_get_bool_vector,
+ poldiff_bool_get_form,
+ poldiff_bool_to_string,
+ bool_reset,
+ bool_get_items,
+ bool_comp,
+ bool_new_diff,
+ bool_deep_diff,
+ },
+ {
+ "category",
+ POLDIFF_DIFF_CATS,
+ poldiff_cat_get_stats,
+ poldiff_get_cat_vector,
+ poldiff_cat_get_form,
+ poldiff_cat_to_string,
+ cat_reset,
+ cat_get_items,
+ cat_comp,
+ cat_new_diff,
+ cat_deep_diff,
+ },
+ {
+ "class",
+ POLDIFF_DIFF_CLASSES,
+ poldiff_class_get_stats,
+ poldiff_get_class_vector,
+ poldiff_class_get_form,
+ poldiff_class_to_string,
+ class_reset,
+ class_get_items,
+ class_comp,
+ class_new_diff,
+ class_deep_diff,
+ },
+ {
+ "common",
+ POLDIFF_DIFF_COMMONS,
+ poldiff_common_get_stats,
+ poldiff_get_common_vector,
+ poldiff_common_get_form,
+ poldiff_common_to_string,
+ common_reset,
+ common_get_items,
+ common_comp,
+ common_new_diff,
+ common_deep_diff,
+ },
+ {
+ "level",
+ POLDIFF_DIFF_LEVELS,
+ poldiff_level_get_stats,
+ poldiff_get_level_vector,
+ poldiff_level_get_form,
+ poldiff_level_to_string,
+ level_reset,
+ level_get_items,
+ level_comp,
+ level_new_diff,
+ level_deep_diff,
+ },
+ {
+ "range transition",
+ POLDIFF_DIFF_RANGE_TRANS,
+ poldiff_range_trans_get_stats,
+ poldiff_get_range_trans_vector,
+ poldiff_range_trans_get_form,
+ poldiff_range_trans_to_string,
+ range_trans_reset,
+ range_trans_get_items,
+ range_trans_comp,
+ range_trans_new_diff,
+ range_trans_deep_diff,
+ },
+ {
+ "role",
+ POLDIFF_DIFF_ROLES,
+ poldiff_role_get_stats,
+ poldiff_get_role_vector,
+ poldiff_role_get_form,
+ poldiff_role_to_string,
+ role_reset,
+ role_get_items,
+ role_comp,
+ role_new_diff,
+ role_deep_diff,
+ },
+ {
+ "role_allow",
+ POLDIFF_DIFF_ROLE_ALLOWS,
+ poldiff_role_allow_get_stats,
+ poldiff_get_role_allow_vector,
+ poldiff_role_allow_get_form,
+ poldiff_role_allow_to_string,
+ role_allow_reset,
+ role_allow_get_items,
+ role_allow_comp,
+ role_allow_new_diff,
+ role_allow_deep_diff,
+ },
+ {
+ "role_transition",
+ POLDIFF_DIFF_ROLE_TRANS,
+ poldiff_role_trans_get_stats,
+ poldiff_get_role_trans_vector,
+ poldiff_role_trans_get_form,
+ poldiff_role_trans_to_string,
+ role_trans_reset,
+ role_trans_get_items,
+ role_trans_comp,
+ role_trans_new_diff,
+ role_trans_deep_diff,
+ },
+ {
+ "Type Change rules",
+ POLDIFF_DIFF_TECHANGE,
+ poldiff_terule_get_stats_change,
+ poldiff_get_terule_vector_change,
+ poldiff_terule_get_form,
+ poldiff_terule_to_string,
+ terule_reset_change,
+ terule_get_items_change,
+ terule_comp,
+ terule_new_diff_change,
+ terule_deep_diff_change,
+ },
+ {
+ "Type Member Rules",
+ POLDIFF_DIFF_TEMEMBER,
+ poldiff_terule_get_stats_member,
+ poldiff_get_terule_vector_member,
+ poldiff_terule_get_form,
+ poldiff_terule_to_string,
+ terule_reset_member,
+ terule_get_items_member,
+ terule_comp,
+ terule_new_diff_member,
+ terule_deep_diff_member,
+ },
+ {
+ "Type Transition Rules",
+ POLDIFF_DIFF_TETRANS,
+ poldiff_terule_get_stats_trans,
+ poldiff_get_terule_vector_trans,
+ poldiff_terule_get_form,
+ poldiff_terule_to_string,
+ terule_reset_trans,
+ terule_get_items_trans,
+ terule_comp,
+ terule_new_diff_trans,
+ terule_deep_diff_trans,
+ },
+ {
+ "type",
+ POLDIFF_DIFF_TYPES,
+ poldiff_type_get_stats,
+ poldiff_get_type_vector,
+ poldiff_type_get_form,
+ poldiff_type_to_string,
+ type_reset,
+ type_get_items,
+ type_comp,
+ type_new_diff,
+ type_deep_diff,
+ },
+ {
+ "user",
+ POLDIFF_DIFF_USERS,
+ poldiff_user_get_stats,
+ poldiff_get_user_vector,
+ poldiff_user_get_form,
+ poldiff_user_to_string,
+ user_reset,
+ user_get_items,
+ user_comp,
+ user_new_diff,
+ user_deep_diff,
+ },
+};
+
+const poldiff_component_record_t *poldiff_get_component_record(uint32_t which)
+{
+ size_t i = 0;
+ size_t num_items;
+
+ num_items = sizeof(component_records) / sizeof(poldiff_component_record_t);
+ for (i = 0; i < num_items; i++) {
+ if (component_records[i].flag_bit == which)
+ return &component_records[i];
+ }
+ return NULL;
+}
+
+poldiff_t *poldiff_create(apol_policy_t * orig_policy, apol_policy_t * mod_policy, poldiff_handle_fn_t fn, void *callback_arg)
+{
+ poldiff_t *diff = NULL;
+ int error;
+
+ if (!orig_policy || !mod_policy) {
+ ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!(diff = calloc(1, sizeof(poldiff_t)))) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+ }
+ diff->orig_pol = orig_policy;
+ diff->mod_pol = mod_policy;
+ diff->orig_qpol = apol_policy_get_qpol(diff->orig_pol);
+ diff->mod_qpol = apol_policy_get_qpol(diff->mod_pol);
+ diff->fn = fn;
+ diff->handle_arg = callback_arg;
+ if ((diff->type_map = type_map_create()) == NULL) {
+ ERR(diff, "%s", strerror(ENOMEM));
+ poldiff_destroy(&diff);
+ errno = ENOMEM;
+ return NULL;
+ }
+ if (type_map_infer(diff) < 0) {
+ error = errno;
+ poldiff_destroy(&diff);
+ errno = error;
+ return NULL;
+ }
+
+ if ((diff->attrib_diffs = attrib_summary_create()) == NULL ||
+ (diff->avrule_diffs[AVRULE_OFFSET_ALLOW] = avrule_create()) == NULL ||
+ (diff->avrule_diffs[AVRULE_OFFSET_AUDITALLOW] = avrule_create()) == NULL ||
+ (diff->avrule_diffs[AVRULE_OFFSET_DONTAUDIT] = avrule_create()) == NULL ||
+ (diff->avrule_diffs[AVRULE_OFFSET_NEVERALLOW] = avrule_create()) == NULL ||
+ (diff->bool_diffs = bool_create()) == NULL ||
+ (diff->cat_diffs = cat_create()) == NULL ||
+ (diff->class_diffs = class_create()) == NULL ||
+ (diff->common_diffs = common_create()) == NULL ||
+ (diff->level_diffs = level_create()) == NULL ||
+ (diff->range_trans_diffs = range_trans_create()) == NULL ||
+ (diff->role_diffs = role_create()) == NULL ||
+ (diff->role_allow_diffs = role_allow_create()) == NULL ||
+ (diff->role_trans_diffs = role_trans_create()) == NULL ||
+ (diff->terule_diffs[TERULE_OFFSET_CHANGE] = terule_create()) == NULL ||
+ (diff->terule_diffs[TERULE_OFFSET_MEMBER] = terule_create()) == NULL ||
+ (diff->terule_diffs[TERULE_OFFSET_TRANS] = terule_create()) == NULL ||
+ (diff->type_diffs = type_summary_create()) == NULL || (diff->user_diffs = user_create()) == NULL) {
+ ERR(diff, "%s", strerror(ENOMEM));
+ poldiff_destroy(&diff);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ diff->policy_opts = QPOL_POLICY_OPTION_NO_RULES | QPOL_POLICY_OPTION_NO_NEVERALLOWS;
+ return diff;
+}
+
+void poldiff_destroy(poldiff_t ** diff)
+{
+ if (!diff || !(*diff))
+ return;
+ apol_policy_destroy(&(*diff)->orig_pol);
+ apol_policy_destroy(&(*diff)->mod_pol);
+ apol_bst_destroy(&(*diff)->class_bst);
+ apol_bst_destroy(&(*diff)->perm_bst);
+ apol_bst_destroy(&(*diff)->bool_bst);
+
+ type_map_destroy(&(*diff)->type_map);
+ attrib_summary_destroy(&(*diff)->attrib_diffs);
+ avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_ALLOW]);
+ avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_AUDITALLOW]);
+ avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_DONTAUDIT]);
+ avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_NEVERALLOW]);
+ bool_destroy(&(*diff)->bool_diffs);
+ cat_destroy(&(*diff)->cat_diffs);
+ class_destroy(&(*diff)->class_diffs);
+ common_destroy(&(*diff)->common_diffs);
+ level_destroy(&(*diff)->level_diffs);
+ range_trans_destroy(&(*diff)->range_trans_diffs);
+ role_destroy(&(*diff)->role_diffs);
+ role_allow_destroy(&(*diff)->role_allow_diffs);
+ role_trans_destroy(&(*diff)->role_trans_diffs);
+ user_destroy(&(*diff)->user_diffs);
+ terule_destroy(&(*diff)->terule_diffs[TERULE_OFFSET_CHANGE]);
+ terule_destroy(&(*diff)->terule_diffs[TERULE_OFFSET_MEMBER]);
+ terule_destroy(&(*diff)->terule_diffs[TERULE_OFFSET_TRANS]);
+ type_summary_destroy(&(*diff)->type_diffs);
+ free(*diff);
+ *diff = NULL;
+}
+
+/**
+ * Given a particular policy item record (e.g., one for object
+ * classes), (re-)perform a diff of them between the two policies
+ * listed in the poldiff_t structure. Upon success, set the status
+ * flag within 'diff' to indicate that this diff is done.
+ *
+ * @param diff The policy difference structure containing the policies
+ * to compare and to populate with the item differences.
+ * @param component_record Item record containg callbacks to perform each
+ * step of the computation for a particular kind of item.
+ *
+ * @return 0 on success and < 0 on error; if the call fails; errno
+ * will be set and the only defined operation on the policy difference
+ * structure will be poldiff_destroy().
+ */
+static int poldiff_do_item_diff(poldiff_t * diff, const poldiff_component_record_t * component_record)
+{
+ apol_vector_t *p1_v = NULL, *p2_v = NULL;
+ int error = 0, retv;
+ size_t x = 0, y = 0;
+ void *item_x = NULL, *item_y = NULL;
+
+ if (!diff || !component_record) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ diff->diff_status &= (~component_record->flag_bit);
+
+ INFO(diff, "Getting %s items from original policy.", component_record->item_name);
+ p1_v = component_record->get_items(diff, diff->orig_pol);
+ if (!p1_v) {
+ error = errno;
+ goto err;
+ }
+
+ INFO(diff, "Getting %s items from modified policy.", component_record->item_name);
+ p2_v = component_record->get_items(diff, diff->mod_pol);
+ if (!p2_v) {
+ error = errno;
+ goto err;
+ }
+
+ INFO(diff, "Finding differences in %s.", component_record->item_name);
+ for (x = 0, y = 0; x < apol_vector_get_size(p1_v);) {
+ if (y >= apol_vector_get_size(p2_v))
+ break;
+ item_x = apol_vector_get_element(p1_v, x);
+ item_y = apol_vector_get_element(p2_v, y);
+ retv = component_record->comp(item_x, item_y, diff);
+ if (retv < 0) {
+ if (component_record->new_diff(diff, POLDIFF_FORM_REMOVED, item_x)) {
+ error = errno;
+ goto err;
+ }
+ x++;
+ } else if (retv > 0) {
+ if (component_record->new_diff(diff, POLDIFF_FORM_ADDED, item_y)) {
+ error = errno;
+ goto err;
+ }
+ y++;
+ } else {
+ if (component_record->deep_diff(diff, item_x, item_y)) {
+ error = errno;
+ goto err;
+ }
+ x++;
+ y++;
+ }
+ }
+ for (; x < apol_vector_get_size(p1_v); x++) {
+ item_x = apol_vector_get_element(p1_v, x);
+ if (component_record->new_diff(diff, POLDIFF_FORM_REMOVED, item_x)) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (; y < apol_vector_get_size(p2_v); y++) {
+ item_y = apol_vector_get_element(p2_v, y);
+ if (component_record->new_diff(diff, POLDIFF_FORM_ADDED, item_y)) {
+ error = errno;
+ goto err;
+ }
+ }
+
+ apol_vector_destroy(&p1_v);
+ apol_vector_destroy(&p2_v);
+ diff->diff_status |= component_record->flag_bit;
+ return 0;
+ err:
+ apol_vector_destroy(&p1_v);
+ apol_vector_destroy(&p2_v);
+ errno = error;
+ return -1;
+}
+
+int poldiff_run(poldiff_t * diff, uint32_t flags)
+{
+ size_t i, num_items;
+
+ if (!flags)
+ return 0; /* nothing to do */
+
+ if (!diff) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ int policy_opts = diff->policy_opts;
+ if (flags & (POLDIFF_DIFF_AVRULES | POLDIFF_DIFF_TERULES)) {
+ policy_opts &= ~(QPOL_POLICY_OPTION_NO_RULES);
+ }
+ if (flags & POLDIFF_DIFF_AVNEVERALLOW) {
+ policy_opts &= ~(QPOL_POLICY_OPTION_NO_NEVERALLOWS);
+ }
+ if (policy_opts != diff->policy_opts) {
+ INFO(diff, "%s", "Loading rules from original policy.");
+ if (qpol_policy_rebuild(diff->orig_qpol, policy_opts)) {
+ return -1;
+ }
+ INFO(diff, "%s", "Loading rules from modified policy.");
+ if (qpol_policy_rebuild(diff->mod_qpol, policy_opts)) {
+ return -1;
+ }
+ // force flushing of existing pointers into policies
+ diff->remapped = 1;
+ diff->policy_opts = policy_opts;
+ }
+
+ num_items = sizeof(component_records) / sizeof(poldiff_component_record_t);
+ if (diff->remapped) {
+ for (i = 0; i < num_items; i++) {
+ if (component_records[i].flag_bit & POLDIFF_DIFF_REMAPPED) {
+ INFO(diff, "Resetting %s diff.", component_records[i].item_name);
+ if (component_records[i].reset(diff))
+ return -1;
+ }
+ }
+ diff->diff_status &= ~(POLDIFF_DIFF_REMAPPED);
+ diff->remapped = 0;
+ }
+
+ INFO(diff, "%s", "Building type map.");
+ if (type_map_build(diff)) {
+ return -1;
+ }
+
+ diff->line_numbers_enabled = 0;
+ for (i = 0; i < num_items; i++) {
+ /* item requested but not yet run */
+ if ((flags & component_records[i].flag_bit) && !(component_records[i].flag_bit & diff->diff_status)) {
+ INFO(diff, "Running %s diff.", component_records[i].item_name);
+ if (poldiff_do_item_diff(diff, &(component_records[i]))) {
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int poldiff_is_run(const poldiff_t * diff, uint32_t flags)
+{
+ if (!flags)
+ return 1; /* nothing to do */
+
+ if (!diff) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if ((diff->diff_status & flags) == flags) {
+ return 1;
+ }
+ return 0;
+}
+
+int poldiff_get_stats(const poldiff_t * diff, uint32_t flags, size_t stats[5])
+{
+ size_t i, j, num_items, tmp_stats[5] = { 0, 0, 0, 0, 0 };
+
+ if (!diff || !flags) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ stats[0] = stats[1] = stats[2] = stats[3] = stats[4] = 0;
+
+ num_items = sizeof(component_records) / sizeof(poldiff_component_record_t);
+ for (i = 0; i < num_items; i++) {
+ if (flags & component_records[i].flag_bit) {
+ component_records[i].get_stats(diff, tmp_stats);
+ for (j = 0; j < 5; j++)
+ stats[j] += tmp_stats[j];
+ }
+ }
+
+ return 0;
+}
+
+int poldiff_enable_line_numbers(poldiff_t * diff)
+{
+ int retval;
+ if (diff == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!diff->line_numbers_enabled) {
+ if (qpol_policy_build_syn_rule_table(diff->orig_qpol))
+ return -1;
+ if (qpol_policy_build_syn_rule_table(diff->mod_qpol))
+ return -1;
+ if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_ALLOW)) < 0) {
+ return retval;
+ }
+ if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_AUDITALLOW)) < 0) {
+ return retval;
+ }
+ if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_DONTAUDIT)) < 0) {
+ return retval;
+ }
+ if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_NEVERALLOW)) < 0) {
+ return retval;
+ }
+ if ((retval = terule_enable_line_numbers(diff, TERULE_OFFSET_CHANGE)) < 0) {
+ return retval;
+ }
+ if ((retval = terule_enable_line_numbers(diff, TERULE_OFFSET_MEMBER)) < 0) {
+ return retval;
+ }
+ if ((retval = terule_enable_line_numbers(diff, TERULE_OFFSET_TRANS)) < 0) {
+ return retval;
+ }
+ diff->line_numbers_enabled = 1;
+ }
+ return 0;
+}
+
+int poldiff_build_bsts(poldiff_t * diff)
+{
+ apol_vector_t *classes[2] = { NULL, NULL };
+ apol_vector_t *perms[2] = { NULL, NULL };
+ apol_vector_t *bools[2] = { NULL, NULL };
+ size_t i, j;
+ const qpol_class_t *cls;
+ qpol_bool_t *qbool;
+ const char *name;
+ char *new_name;
+ int retval = -1, error = 0;
+ if (diff->class_bst != NULL) {
+ return 0;
+ }
+ if ((diff->class_bst = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (diff->perm_bst = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (diff->bool_bst = apol_bst_create(apol_str_strcmp, free)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < 2; i++) {
+ apol_policy_t *p = (i == 0 ? diff->orig_pol : diff->mod_pol);
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ if (apol_class_get_by_query(p, NULL, &classes[i]) < 0 ||
+ apol_perm_get_by_query(p, NULL, &perms[i]) < 0 || apol_bool_get_by_query(p, NULL, &bools[i]) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (j = 0; j < apol_vector_get_size(classes[i]); j++) {
+ cls = apol_vector_get_element(classes[i], j);
+ if (qpol_class_get_name(q, cls, &name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((new_name = strdup(name)) == NULL ||
+ apol_bst_insert_and_get(diff->class_bst, (void **)&new_name, NULL) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (j = 0; j < apol_vector_get_size(perms[i]); j++) {
+ name = (char *)apol_vector_get_element(perms[i], j);
+ if ((new_name = strdup(name)) == NULL ||
+ apol_bst_insert_and_get(diff->perm_bst, (void **)&new_name, NULL) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (j = 0; j < apol_vector_get_size(bools[i]); j++) {
+ qbool = (qpol_bool_t *) apol_vector_get_element(bools[i], j);
+ if (qpol_bool_get_name(q, qbool, &name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((new_name = strdup(name)) == NULL ||
+ apol_bst_insert_and_get(diff->bool_bst, (void **)&new_name, NULL) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&classes[0]);
+ apol_vector_destroy(&classes[1]);
+ apol_vector_destroy(&perms[0]);
+ apol_vector_destroy(&perms[1]);
+ apol_vector_destroy(&bools[0]);
+ apol_vector_destroy(&bools[1]);
+ errno = error;
+ return retval;
+}
+
+static void poldiff_handle_default_callback(void *arg __attribute__ ((unused)),
+ poldiff_t * p __attribute__ ((unused)), int level, const char *fmt, va_list va_args)
+{
+ switch (level) {
+ case POLDIFF_MSG_INFO:
+ {
+ /* by default do not display these messages */
+ return;
+ }
+ case POLDIFF_MSG_WARN:
+ {
+ fprintf(stderr, "WARNING: ");
+ break;
+ }
+ case POLDIFF_MSG_ERR:
+ default:
+ {
+ fprintf(stderr, "ERROR: ");
+ break;
+ }
+ }
+ vfprintf(stderr, fmt, va_args);
+ fprintf(stderr, "\n");
+}
+
+void poldiff_handle_msg(const poldiff_t * p, int level, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ if (p == NULL || p->fn == NULL) {
+ poldiff_handle_default_callback(NULL, NULL, level, fmt, ap);
+ } else {
+ p->fn(p->handle_arg, p, level, fmt, ap);
+ }
+ va_end(ap);
+}
+
+poldiff_item_get_form_fn_t poldiff_component_record_get_form_fn(const poldiff_component_record_t * diff)
+{
+ if (!diff) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->get_form;
+}
+
+poldiff_item_to_string_fn_t poldiff_component_record_get_to_string_fn(const poldiff_component_record_t * diff)
+{
+ if (!diff) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->to_string;
+}
+
+poldiff_get_item_stats_fn_t poldiff_component_record_get_stats_fn(const poldiff_component_record_t * diff)
+{
+ if (!diff) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->get_stats;
+}
+
+poldiff_get_result_items_fn_t poldiff_component_record_get_results_fn(const poldiff_component_record_t * diff)
+{
+ if (!diff) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->get_results;
+}
+
+const char *poldiff_component_record_get_label(const poldiff_component_record_t * diff)
+{
+ if (!diff) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->item_name;
+}
diff --git a/libpoldiff/src/poldiff_internal.h b/libpoldiff/src/poldiff_internal.h
new file mode 100644
index 0000000..e84cdb7
--- /dev/null
+++ b/libpoldiff/src/poldiff_internal.h
@@ -0,0 +1,239 @@
+/**
+ * @file
+ * Protected interface for computing semantic policy difference.
+ *
+ * @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 POLDIFF_POLDIFF_INTERNAL_H
+#define POLDIFF_POLDIFF_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <poldiff/poldiff.h>
+#include <apol/bst.h>
+
+ typedef enum
+ {
+ AVRULE_OFFSET_ALLOW = 0, AVRULE_OFFSET_AUDITALLOW,
+ AVRULE_OFFSET_DONTAUDIT, AVRULE_OFFSET_NEVERALLOW,
+ AVRULE_OFFSET_MAX
+ } avrule_offset_e;
+
+ typedef enum
+ {
+ TERULE_OFFSET_CHANGE = 0, TERULE_OFFSET_MEMBER,
+ TERULE_OFFSET_TRANS,
+ TERULE_OFFSET_MAX
+ } terule_offset_e;
+
+#include "attrib_internal.h"
+#include "avrule_internal.h"
+#include "bool_internal.h"
+#include "cat_internal.h"
+#include "class_internal.h"
+#include "level_internal.h"
+#include "range_internal.h"
+#include "range_trans_internal.h"
+#include "rbac_internal.h"
+#include "role_internal.h"
+#include "terule_internal.h"
+#include "user_internal.h"
+#include "type_internal.h"
+
+#include "type_map_internal.h"
+
+/* forward declarations */
+ struct poldiff_attrib_summary;
+ struct poldiff_avrule_summary;
+ struct poldiff_bool_summary;
+ struct poldiff_cat_summary;
+ struct poldiff_class_summary;
+ struct poldiff_common_summary;
+ struct poldiff_level_summary;
+ struct poldiff_range_trans_summary;
+ struct poldiff_role_summary;
+ struct poldiff_role_allow_summary;
+ struct poldiff_role_trans_summary;
+ struct poldiff_terule_summary;
+ struct poldiff_type_summary;
+ struct poldiff_user_summary;
+/* and so forth for ocon_summary structs */
+
+ struct poldiff
+ {
+ /** the "original" policy */
+ apol_policy_t *orig_pol;
+ /** the "modified" policy */
+ apol_policy_t *mod_pol;
+ /** pointer to original's qpol policy within orig_pol */
+ qpol_policy_t *orig_qpol;
+ /** pointer to modified's qpol policy within mod_pol */
+ qpol_policy_t *mod_qpol;
+ /** non-zero if rules' line numbers are accurate */
+ int line_numbers_enabled;
+ /** BST of duplicated strings, used when making pseudo-rules */
+ apol_bst_t *class_bst;
+ /** BST of duplicated strings, used when making pseudo-rules */
+ apol_bst_t *perm_bst;
+ /** BST of duplicated strings, used when making pseudo-rules */
+ apol_bst_t *bool_bst;
+ poldiff_handle_fn_t fn;
+ void *handle_arg;
+ /** set of POLDIF_DIFF_* bits for diffs run */
+ uint32_t diff_status;
+ struct poldiff_attrib_summary *attrib_diffs;
+ struct poldiff_avrule_summary *avrule_diffs[AVRULE_OFFSET_MAX];
+ struct poldiff_bool_summary *bool_diffs;
+ struct poldiff_cat_summary *cat_diffs;
+ struct poldiff_class_summary *class_diffs;
+ struct poldiff_common_summary *common_diffs;
+ struct poldiff_level_summary *level_diffs;
+ struct poldiff_range_trans_summary *range_trans_diffs;
+ struct poldiff_role_summary *role_diffs;
+ struct poldiff_role_allow_summary *role_allow_diffs;
+ struct poldiff_role_trans_summary *role_trans_diffs;
+ struct poldiff_terule_summary *terule_diffs[TERULE_OFFSET_MAX];
+ struct poldiff_type_summary *type_diffs;
+ struct poldiff_user_summary *user_diffs;
+ /* and so forth if we want ocon_diffs */
+ type_map_t *type_map;
+ /** most recently used flags to open the two policies */
+ int policy_opts;
+ /** set if type mapping was changed since last run */
+ int remapped;
+ };
+
+/**
+ * Callback function signature for getting a vector of all unique
+ * items of a given kind in a policy. The vector must be sorted
+ * prior to returning from this function.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ * @return a newly allocated vector of all unique items of the
+ * appropriate kind on success, or NULL on error; if the call fails,
+ * errno will be set.
+ */
+ typedef apol_vector_t *(*poldiff_get_items_fn_t) (poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Callback function signature for quickly comparing two items to
+ * determine if they are semantically the same item. This operation
+ * should quickly determine if the two are obviously different or
+ * not.
+ *
+ * @param x The item from the original policy.
+ * @param y The item from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * items.
+ *
+ * @return Expected return value from this function is < 0, 0, or > 0
+ * if item x is respectively less than, equal to, or greater than item y.
+ * This must be able to return a defined stable ordering for all items
+ * not semantically equivalent.
+ */
+ typedef int (*poldiff_item_comp_fn_t) (const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Callback function signature for creating, initializing and inserting
+ * a new semantic difference entry for an item.
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference, one of POLDIFF_FORM_ADDED or
+ * POLDIFF_FORM_REMOVED.
+ * @param item Item for which the entry is being created.
+ * @return Expected return value from this function is 0 on success and
+ * < 0 on error; if the call fails, it is expected to set errno and to
+ * leave the policy difference structure unchanged.
+ */
+ typedef int (*poldiff_new_diff_fn_t) (poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Callback function signature for computing the semantic difference of
+ * two items for which the compare callback returns 0. This function should
+ * calculate the difference of any properties of the items and if a difference
+ * is found to allocate, initialize, and insert an new semantic difference
+ * entry for that item.
+ * @param diff The policy difference structure associated with both items and
+ * to which to add an entry if needed.
+ * @param x The item from the original policy.
+ * @param y The item from the modified policy.
+ * @return Expected return value from this function is 0 on success and
+ * < 0 on error; if the call fails, it is expected to set errno and to
+ * leave the policy difference structure unchanged.
+ */
+ typedef int (*poldiff_deep_diff_fn_t) (poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Callback function signature for resetting the diff results for an
+ * item. called when mapping of the symbols used by the diff change.
+ * @param diff The policy difference structure containing the diffs
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * it is expected to set errno.
+ */
+ typedef int (*poldiff_reset_fn_t) (poldiff_t * diff);
+
+/******************** error handling code below ********************/
+
+#define POLDIFF_MSG_ERR 1
+#define POLDIFF_MSG_WARN 2
+#define POLDIFF_MSG_INFO 3
+
+/**
+ * Write a message to the callback stored within a poldiff error
+ * handler. If the msg_callback field is empty then suppress the
+ * message.
+ *
+ * @param p Error reporting handler. If NULL then write message to
+ * stderr.
+ * @param level Severity of message, one of POLDIFF_MSG_ERR,
+ * POLDIFF_MSG_WARN, or POLDIFF_MSG_INFO.
+ * @param fmt Format string to print, using syntax of printf(3).
+ */
+ __attribute__ ((format(printf, 3, 4))) extern void poldiff_handle_msg(const poldiff_t * p, int level, const char *fmt, ...);
+
+#undef ERR
+#undef WARN
+#undef INFO
+
+#define ERR(handle, format, ...) poldiff_handle_msg(handle, POLDIFF_MSG_ERR, format, __VA_ARGS__)
+#define WARN(handle, format, ...) poldiff_handle_msg(handle, POLDIFF_MSG_WARN, format, __VA_ARGS__)
+#define INFO(handle, format, ...) poldiff_handle_msg(handle, POLDIFF_MSG_INFO, format, __VA_ARGS__)
+
+/**
+ * Build the BST for classes, permissions, and booleans if the
+ * policies have changed. This effectively provides a partial mapping
+ * of rules from one policy to the other.
+ *
+ * @param diff Policy difference structure containing policies to diff.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ int poldiff_build_bsts(poldiff_t * diff);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_POLDIFF_INTERNAL_H */
diff --git a/libpoldiff/src/range_diff.c b/libpoldiff/src/range_diff.c
new file mode 100644
index 0000000..e6c7c3a
--- /dev/null
+++ b/libpoldiff/src/range_diff.c
@@ -0,0 +1,420 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in ranges.
+ *
+ * @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 "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+
+struct poldiff_range
+{
+ apol_mls_range_t *orig_range;
+ apol_mls_range_t *mod_range;
+ /** a vector of poldiff_level_t */
+ apol_vector_t *levels;
+ apol_vector_t *min_added_cats;
+ apol_vector_t *min_removed_cats;
+ apol_vector_t *min_unmodified_cats;
+};
+
+apol_vector_t *poldiff_range_get_levels(const poldiff_range_t * range)
+{
+ if (range == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->levels;
+}
+
+const apol_mls_range_t *poldiff_range_get_original_range(const poldiff_range_t * range)
+{
+ if (range == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->orig_range;
+}
+
+const apol_mls_range_t *poldiff_range_get_modified_range(const poldiff_range_t * range)
+{
+ if (range == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->mod_range;
+}
+
+apol_vector_t *poldiff_range_get_min_added_cats(const poldiff_range_t * range)
+{
+ if (range == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->min_added_cats;
+}
+
+apol_vector_t *poldiff_range_get_min_removed_cats(const poldiff_range_t * range)
+{
+ if (range == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->min_removed_cats;
+}
+
+apol_vector_t *poldiff_range_get_min_unmodified_cats(const poldiff_range_t * range)
+{
+ if (range == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->min_unmodified_cats;
+}
+
+char *poldiff_range_to_string_brief(const poldiff_t * diff, const poldiff_range_t * range)
+{
+ char *r1 = NULL, *r2 = NULL;
+ char *s = NULL, *t = NULL, *sep = "", *cat;
+ size_t len = 0, i;
+ if (range->orig_range != NULL && (r1 = apol_mls_range_render(diff->orig_pol, range->orig_range)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (range->mod_range != NULL && (r2 = apol_mls_range_render(diff->mod_pol, range->mod_range)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ assert(r1 != NULL || r2 != NULL);
+ if (r1 == NULL) {
+ if (apol_str_appendf(&s, &len, " range: %s\n", r2) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ } else if (r2 == NULL) {
+ if (apol_str_appendf(&s, &len, " range: %s\n", r1) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ } else {
+ if (apol_str_appendf(&s, &len, " range: %s --> %s\n", r1, r2) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ if ((range->min_added_cats != NULL && apol_vector_get_size(range->min_added_cats) > 0) ||
+ (range->min_removed_cats != NULL && apol_vector_get_size(range->min_removed_cats) > 0) ||
+ (range->min_unmodified_cats != NULL && apol_vector_get_size(range->min_unmodified_cats) > 0)) {
+ if (apol_str_append(&s, &len, " minimum categories: ") < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (i = 0; range->min_unmodified_cats != NULL && i < apol_vector_get_size(range->min_unmodified_cats); i++) {
+ cat = apol_vector_get_element(range->min_unmodified_cats, i);
+ if (apol_str_appendf(&s, &len, "%s%s", sep, cat) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ sep = ",";
+ }
+ for (i = 0; range->min_added_cats != NULL && i < apol_vector_get_size(range->min_added_cats); i++) {
+ cat = apol_vector_get_element(range->min_added_cats, i);
+ if (apol_str_appendf(&s, &len, "%s+%s", sep, cat) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ sep = ",";
+ }
+ for (i = 0; range->min_removed_cats != NULL && i < apol_vector_get_size(range->min_removed_cats); i++) {
+ cat = apol_vector_get_element(range->min_removed_cats, i);
+ if (apol_str_appendf(&s, &len, "%s-%s", sep, cat) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ sep = ",";
+ }
+ if (apol_str_append(&s, &len, "\n") < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(range->levels); i++) {
+ poldiff_level_t *level = apol_vector_get_element(range->levels, i);
+ if ((t = poldiff_level_to_string_brief(diff, level)) == NULL) {
+ goto cleanup;
+ }
+ if (apol_str_appendf(&s, &len, " %s", t) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ free(t);
+ t = NULL;
+ }
+ cleanup:
+ free(r1);
+ free(r2);
+ free(t);
+ return s;
+}
+
+poldiff_range_t *range_create(const poldiff_t * diff, const qpol_mls_range_t * orig_range, const qpol_mls_range_t * mod_range,
+ poldiff_form_e form)
+{
+ poldiff_range_t *pr = NULL;
+ apol_policy_t *p;
+ apol_mls_range_t *range;
+ apol_vector_t *levels = NULL;
+ poldiff_level_t *pl = NULL;
+ size_t i;
+ int retval = -1;
+ if ((pr = calloc(1, sizeof(*pr))) == NULL || (pr->levels = apol_vector_create(level_free)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (orig_range != NULL && (pr->orig_range = apol_mls_range_create_from_qpol_mls_range(diff->orig_pol, orig_range)) == NULL) {
+ goto cleanup;
+ }
+ if (mod_range != NULL && (pr->mod_range = apol_mls_range_create_from_qpol_mls_range(diff->mod_pol, mod_range)) == NULL) {
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ p = diff->mod_pol;
+ range = pr->mod_range;
+ } else if (form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_REMOVE_TYPE) {
+ p = diff->orig_pol;
+ range = pr->orig_range;
+ } else if (form == POLDIFF_FORM_MODIFIED) {
+ /* don't fill in the range's levels here */
+ return pr;
+ } else {
+ /* should never get here */
+ assert(0);
+ return pr;
+ }
+ if ((levels = apol_mls_range_get_levels(p, range)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(levels); i++) {
+ apol_mls_level_t *l = apol_vector_get_element(levels, i);
+ const char *sens = apol_mls_level_get_sens(l);
+ const apol_vector_t *cats = apol_mls_level_get_cats(l);
+ if ((pl = calloc(1, sizeof(*pl))) == NULL ||
+ (pl->name = strdup(sens)) == NULL || (pl->unmodified_cats = apol_vector_create_with_capacity(1, free)) == NULL)
+ {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ if ((pl->added_cats = apol_vector_create_from_vector(cats, apol_str_strdup, NULL, free)) == NULL ||
+ (pl->removed_cats = apol_vector_create_with_capacity(1, free)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ } else if (form == POLDIFF_FORM_REMOVED) {
+ if ((pl->added_cats = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pl->removed_cats = apol_vector_create_from_vector(cats, apol_str_strdup, NULL, free)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_append(pr->levels, pl) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ pl = NULL;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&levels);
+ if (retval != 0) {
+ level_free(pl);
+ range_destroy(&pr);
+ return NULL;
+ }
+ return pr;
+}
+
+void range_destroy(poldiff_range_t ** range)
+{
+ if (range != NULL && *range != NULL) {
+ apol_mls_range_destroy(&(*range)->orig_range);
+ apol_mls_range_destroy(&(*range)->mod_range);
+ apol_vector_destroy(&(*range)->levels);
+ apol_vector_destroy(&(*range)->min_added_cats);
+ apol_vector_destroy(&(*range)->min_removed_cats);
+ apol_vector_destroy(&(*range)->min_unmodified_cats);
+ free(*range);
+ *range = NULL;
+ }
+}
+
+/**
+ * Comparison function for two apol_mls_level_t objects from the same
+ * apol_mls_range_t. Sorts the levels in alphabetical order according
+ * to sensitivity.
+ */
+static int range_comp_alphabetize(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const apol_mls_level_t *l1 = a;
+ const apol_mls_level_t *l2 = b;
+ const char *sens1 = apol_mls_level_get_sens(l1);
+ const char *sens2 = apol_mls_level_get_sens(l2);
+ return strcmp(sens1, sens2);
+}
+
+/**
+ * Comparison function for two levels from the same poldiff_range_t.
+ * Sorts the levels by form; within each form sort them by policy
+ * order.
+ */
+static int range_comp(const void *a, const void *b, void *data)
+{
+ const poldiff_level_t *l1 = a;
+ const poldiff_level_t *l2 = b;
+ poldiff_t *diff = data;
+ qpol_policy_t *q;
+ const qpol_level_t *ql1, *ql2;
+ uint32_t v1, v2;
+ if (l1->form != l2->form) {
+ return l1->form - l2->form;
+ }
+ if (l1->form == POLDIFF_FORM_ADDED) {
+ q = diff->mod_qpol;
+ } else {
+ q = diff->orig_qpol;
+ }
+ qpol_policy_get_level_by_name(q, l1->name, &ql1);
+ qpol_policy_get_level_by_name(q, l2->name, &ql2);
+ qpol_level_get_value(q, ql1, &v1);
+ qpol_level_get_value(q, ql2, &v2);
+ assert(v1 != 0 && v2 != 0);
+ return v1 - v2;
+}
+
+int range_deep_diff(poldiff_t * diff, poldiff_range_t * range)
+{
+ apol_vector_t *orig_levels = NULL, *mod_levels = NULL;
+ apol_vector_t *added = NULL, *removed = NULL, *unmodified = NULL;
+ apol_mls_level_t *l1, *l2;
+ poldiff_level_t *pl1, *pl2;
+ size_t i, j;
+ int retval = -1, differences_found = 0, compval;
+ if ((orig_levels = apol_mls_range_get_levels(diff->orig_pol, range->orig_range)) == NULL ||
+ (mod_levels = apol_mls_range_get_levels(diff->mod_pol, range->mod_range)) == NULL) {
+ goto cleanup;
+ }
+ apol_vector_sort(orig_levels, range_comp_alphabetize, NULL);
+ apol_vector_sort(mod_levels, range_comp_alphabetize, NULL);
+ for (i = j = 0; i < apol_vector_get_size(orig_levels);) {
+ if (j >= apol_vector_get_size(mod_levels))
+ break;
+ l1 = (apol_mls_level_t *) apol_vector_get_element(orig_levels, i);
+ l2 = (apol_mls_level_t *) apol_vector_get_element(mod_levels, j);
+ pl1 = pl2 = NULL;
+ const char *sens1 = apol_mls_level_get_sens(l1);
+ const char *sens2 = apol_mls_level_get_sens(l2);
+ compval = strcmp(sens1, sens2);
+ if (compval < 0) {
+ if ((pl1 = level_create_from_apol_mls_level(l1, POLDIFF_FORM_REMOVED)) == NULL
+ || apol_vector_append(range->levels, pl1) < 0) {
+ level_free(pl1);
+ goto cleanup;
+ }
+ differences_found = 1;
+ i++;
+ } else if (compval > 0) {
+ if ((pl2 = level_create_from_apol_mls_level(l2, POLDIFF_FORM_ADDED)) == NULL
+ || apol_vector_append(range->levels, pl2) < 0) {
+ level_free(pl2);
+ goto cleanup;
+ }
+ differences_found = 1;
+ j++;
+ } else {
+ if (level_deep_diff_apol_mls_levels(diff, l1, l2, &pl1, &pl2) < 0) {
+ goto cleanup;
+ }
+ assert(pl2 == NULL);
+ if (pl1 != NULL) {
+ if (apol_vector_append(range->levels, pl1) < 0) {
+ level_free(pl1);
+ goto cleanup;
+ }
+ differences_found = 1;
+ }
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(orig_levels); i++) {
+ l1 = (apol_mls_level_t *) apol_vector_get_element(orig_levels, i);
+ if ((pl1 = level_create_from_apol_mls_level(l1, POLDIFF_FORM_REMOVED)) == NULL
+ || apol_vector_append(range->levels, pl1) < 0) {
+ level_free(pl1);
+ goto cleanup;
+ }
+ differences_found = 1;
+ }
+ for (; j < apol_vector_get_size(mod_levels); j++) {
+ l2 = (apol_mls_level_t *) apol_vector_get_element(mod_levels, j);
+ if ((pl2 = level_create_from_apol_mls_level(l2, POLDIFF_FORM_ADDED)) == NULL
+ || apol_vector_append(range->levels, pl2) < 0) {
+ level_free(pl2);
+ goto cleanup;
+ }
+ differences_found = 1;
+ }
+ /* now check minimum category sets */
+ const apol_mls_level_t *low1 = apol_mls_range_get_low(range->orig_range);
+ const apol_vector_t *cats1 = apol_mls_level_get_cats(low1);
+ const apol_mls_level_t *low2 = apol_mls_range_get_low(range->mod_range);
+ const apol_vector_t *cats2 = apol_mls_level_get_cats(low2);
+ compval = level_deep_diff_cats(diff, cats1, cats2, &added, &removed, &unmodified);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval > 0) {
+ differences_found = 1;
+ range->min_added_cats = added;
+ range->min_removed_cats = removed;
+ range->min_unmodified_cats = unmodified;
+ added = NULL;
+ removed = NULL;
+ unmodified = NULL;
+ }
+ if (differences_found) {
+ apol_vector_sort(range->levels, range_comp, diff);
+ retval = 1;
+ } else {
+ retval = 0;
+ }
+ cleanup:
+ apol_vector_destroy(&orig_levels);
+ apol_vector_destroy(&mod_levels);
+ apol_vector_destroy(&added);
+ apol_vector_destroy(&removed);
+ apol_vector_destroy(&unmodified);
+ return retval;
+}
diff --git a/libpoldiff/src/range_internal.h b/libpoldiff/src/range_internal.h
new file mode 100644
index 0000000..bf4d296
--- /dev/null
+++ b/libpoldiff/src/range_internal.h
@@ -0,0 +1,80 @@
+/**
+ * @file
+ * Protected interface for range differences.
+ *
+ * @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 POLDIFF_RANGE_INTERNAL_H
+#define POLDIFF_RANGE_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * Allocate and return a poldiff_range_t object. This will fill in
+ * the orig_range and mod_range strings. If the form is modified,
+ * then this will allocate the levels vector but leave it empty.
+ * Otherwise the levels vector will be filled with the levels that
+ * were added/removed.
+ *
+ * @param diff Diff object containing policies.
+ * @param orig_range Range from original policy, or NULL if there is
+ * no original range.
+ * @param mod_range Range from modified policy, or NULL if there is no
+ * modified range.
+ * @param form Form of the range.
+ *
+ * @return An initialized range, or NULL upon error. Caller must call
+ * range_destroy() upon the returned value.
+ */
+ poldiff_range_t *range_create(const poldiff_t * diff, const qpol_mls_range_t * orig_range,
+ const qpol_mls_range_t * mod_range, poldiff_form_e form);
+
+/**
+ * Deallocate all space for a range, including the pointer itself.
+ * Afterwards set the pointer to NULL.
+ *
+ * @param range Reference to a range to destroy.
+ */
+ void range_destroy(poldiff_range_t ** range);
+
+/**
+ * Calculate the differences between two ranges (that are stored
+ * within the poldiff_range_t object). This involves two things:
+ * changes in the expanded levels, and changes to minimum category
+ * sets. If differences are found then the range's levels vector will
+ * be filled with those differences.
+ *
+ * @param diff Diff object containing policies.
+ * @param range Range object to diff.
+ *
+ * @return Greater than zero if a diff was found, zero if none found,
+ * less than zero for errors.
+ */
+ int range_deep_diff(poldiff_t * diff, poldiff_range_t * range);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_RANGE_INTERNAL_H */
diff --git a/libpoldiff/src/range_trans_diff.c b/libpoldiff/src/range_trans_diff.c
new file mode 100644
index 0000000..7394c89
--- /dev/null
+++ b/libpoldiff/src/range_trans_diff.c
@@ -0,0 +1,520 @@
+/**
+ * @file
+ * Implementation for computing a semantic differences in range
+ * transition rules.
+ *
+ * @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 "poldiff_internal.h"
+
+#include <apol/mls-query.h>
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_range_trans_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ size_t num_added_type;
+ size_t num_removed_type;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_range_trans
+{
+ char *source;
+ char *target;
+ char *target_class;
+ poldiff_form_e form;
+ poldiff_range_t *range;
+};
+
+void poldiff_range_trans_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->range_trans_diffs->num_added;
+ stats[1] = diff->range_trans_diffs->num_removed;
+ stats[2] = diff->range_trans_diffs->num_modified;
+ stats[3] = diff->range_trans_diffs->num_added_type;
+ stats[4] = diff->range_trans_diffs->num_removed_type;
+}
+
+char *poldiff_range_trans_to_string(const poldiff_t * diff, const void *range_trans)
+{
+ const poldiff_range_trans_t *rt = range_trans;
+ const poldiff_range_t *range = poldiff_range_trans_get_range(rt);
+ const apol_mls_range_t *orig_range = poldiff_range_get_original_range(range);
+ const apol_mls_range_t *mod_range = poldiff_range_get_modified_range(range);
+ size_t len = 0;
+ char *s = NULL;
+ if (diff == NULL || range_trans == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (rt->form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ char *t = NULL;
+ if ((t = apol_mls_range_render(diff->mod_pol, mod_range)) == NULL ||
+ apol_str_appendf(&s, &len, "+ range_transition %s %s : %s %s;", rt->source, rt->target,
+ rt->target_class, t) < 0) {
+ free(t);
+ goto cleanup;
+ }
+ free(t);
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ char *t = NULL;
+ if ((t = apol_mls_range_render(diff->orig_pol, orig_range)) == NULL ||
+ apol_str_appendf(&s, &len, "- range_transition %s %s : %s %s;", rt->source, rt->target,
+ rt->target_class, t) < 0) {
+ free(t);
+ goto cleanup;
+ }
+ free(t);
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ char *t;
+ if ((t = poldiff_range_to_string_brief(diff, range)) == NULL ||
+ apol_str_appendf(&s, &len, "* range_transition %s %s : %s\n%s", rt->source, rt->target,
+ rt->target_class, t) < 0) {
+ free(t);
+ goto cleanup;
+ }
+ free(t);
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ cleanup:
+ /* if this is reached then an error occurred */
+ ERR(diff, "%s", strerror(ENOMEM));
+ free(s);
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_range_trans_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->range_trans_diffs->diffs;
+}
+
+const char *poldiff_range_trans_get_source_type(const poldiff_range_trans_t * range_trans)
+{
+ if (range_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range_trans->source;
+}
+
+const char *poldiff_range_trans_get_target_type(const poldiff_range_trans_t * range_trans)
+{
+ if (range_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range_trans->target;
+}
+
+const char *poldiff_range_trans_get_target_class(const poldiff_range_trans_t * range_trans)
+{
+ if (range_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range_trans->target_class;
+}
+
+const poldiff_range_t *poldiff_range_trans_get_range(const poldiff_range_trans_t * range_trans)
+{
+ if (range_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range_trans->range;
+}
+
+poldiff_form_e poldiff_range_trans_get_form(const void *range_trans)
+{
+ if (range_trans == NULL) {
+ errno = EINVAL;
+ return POLDIFF_FORM_NONE;
+ }
+ return ((const poldiff_range_trans_t *)range_trans)->form;
+}
+
+/**
+ * Destroy all space used by a poldiff_range_trans_t, including the
+ * pointer itself.
+ */
+static void range_trans_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_range_trans_t *rt = (poldiff_range_trans_t *) elem;
+ free(rt->source);
+ free(rt->target);
+ free(rt->target_class);
+ range_destroy(&rt->range);
+ free(rt);
+ }
+}
+
+poldiff_range_trans_summary_t *range_trans_create(void)
+{
+ poldiff_range_trans_summary_t *rts = calloc(1, sizeof(*rts));
+ if (rts == NULL) {
+ return NULL;
+ }
+ if ((rts->diffs = apol_vector_create(range_trans_free)) == NULL) {
+ range_trans_destroy(&rts);
+ return NULL;
+ }
+ return rts;
+}
+
+void range_trans_destroy(poldiff_range_trans_summary_t ** rts)
+{
+ if (rts != NULL && *rts != NULL) {
+ apol_vector_destroy(&(*rts)->diffs);
+ free(*rts);
+ *rts = NULL;
+ }
+}
+
+typedef struct pseudo_range_trans
+{
+ uint32_t source_type, target_type;
+ /* pointer into a policy's class's symbol table */
+ const char *target_class;
+ const qpol_mls_range_t *range;
+} pseudo_range_trans_t;
+
+static void range_trans_free_item(void *item)
+{
+ if (item != NULL) {
+ pseudo_range_trans_t *prt = item;
+ free(prt);
+ }
+}
+
+int range_trans_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused)))
+{
+ const pseudo_range_trans_t *p1 = x;
+ const pseudo_range_trans_t *p2 = y;
+
+ if (p1->source_type != p2->source_type) {
+ return p1->source_type - p2->source_type;
+ }
+ if (p1->target_type != p2->target_type) {
+ return p1->target_type - p2->target_type;
+ }
+ return strcmp(p1->target_class, p2->target_class);
+}
+
+int range_trans_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ range_trans_destroy(&diff->range_trans_diffs);
+ diff->range_trans_diffs = range_trans_create();
+ if (diff->range_trans_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Allocate and return a new range trans difference object. If the
+ * pseudo-range trans's source and/or target expands to multiple read
+ * types, then just choose the first one for display.
+ */
+static poldiff_range_trans_t *make_range_trans_diff(const poldiff_t * diff, poldiff_form_e form, const pseudo_range_trans_t * prt)
+{
+ poldiff_range_trans_t *rt = NULL;
+ const char *n1, *n2;
+ int error;
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ n1 = type_map_get_name(diff, prt->source_type, POLDIFF_POLICY_MOD);
+ n2 = type_map_get_name(diff, prt->target_type, POLDIFF_POLICY_MOD);
+ } else {
+ n1 = type_map_get_name(diff, prt->source_type, POLDIFF_POLICY_ORIG);
+ n2 = type_map_get_name(diff, prt->target_type, POLDIFF_POLICY_ORIG);
+ }
+ assert(n1 != NULL && n2 != NULL);
+ if ((rt = calloc(1, sizeof(*rt))) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ if ((rt->source = strdup(n1)) == NULL ||
+ (rt->target = strdup(n2)) == NULL || (rt->target_class = strdup(prt->target_class)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(errno));
+ range_trans_free(rt);
+ errno = error;
+ return NULL;
+ }
+ rt->form = form;
+ return rt;
+}
+
+int range_trans_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const pseudo_range_trans_t *prt = (const pseudo_range_trans_t *)item;
+ const apol_vector_t *v1, *v2;
+ const qpol_mls_range_t *orig_range = NULL, *mod_range = NULL;
+ poldiff_range_trans_t *rt = NULL;
+ int error;
+
+ /* check if form should really become ADD_TYPE / REMOVE_TYPE,
+ * by seeing if the /other/ policy's reverse lookup is
+ * empty */
+ if (form == POLDIFF_FORM_ADDED) {
+ if ((v1 = type_map_lookup_reverse(diff, prt->source_type, POLDIFF_POLICY_ORIG)) == NULL ||
+ (v2 = type_map_lookup_reverse(diff, prt->target_type, POLDIFF_POLICY_ORIG)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) {
+ form = POLDIFF_FORM_ADD_TYPE;
+ }
+ mod_range = prt->range;
+ } else {
+ if ((v1 = type_map_lookup_reverse(diff, prt->source_type, POLDIFF_POLICY_MOD)) == NULL ||
+ (v2 = type_map_lookup_reverse(diff, prt->target_type, POLDIFF_POLICY_MOD)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) {
+ form = POLDIFF_FORM_REMOVE_TYPE;
+ }
+ orig_range = prt->range;
+ }
+ if ((rt = make_range_trans_diff(diff, form, prt)) == NULL ||
+ (rt->range = range_create(diff, orig_range, mod_range, form)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_append(diff->range_trans_diffs->diffs, rt) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ /* increment appropriate counter */
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ diff->range_trans_diffs->num_added++;
+ break;
+ }
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ diff->range_trans_diffs->num_added_type++;
+ break;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ diff->range_trans_diffs->num_removed++;
+ break;
+ }
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ diff->range_trans_diffs->num_removed_type++;
+ break;
+ }
+ default:
+ {
+ /* not reachable */
+ assert(0);
+ }
+ }
+ return 0;
+ cleanup:
+ range_trans_free(rt);
+ errno = error;
+ return -1;
+}
+
+/**
+ * Compare two pseudo range transition rules from the same policy.
+ * Compares the pseudo source type, pseudo target type, and target
+ * class.
+ *
+ * @param x A pseudo_range_trans_t entry.
+ * @param y A pseudo_range_trans_t entry.
+ * @param arg The policy difference structure.
+ *
+ * @return < 0, 0, or > 0 if the first rule is respectively less than,
+ * equal to, or greater than the second. If the return value would be 0
+ * but the default role is different a warning is issued.
+ */
+static int pseudo_range_trans_comp(const void *x, const void *y, void *arg)
+{
+ const pseudo_range_trans_t *a = x;
+ const pseudo_range_trans_t *b = y;
+ poldiff_t *diff = arg;
+ int retval = range_trans_comp(a, b, diff);
+ return retval;
+}
+
+apol_vector_t *range_trans_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ apol_vector_t *v = NULL;
+ qpol_iterator_t *iter = NULL;
+ const qpol_range_trans_t *qrt = NULL;
+ const qpol_type_t *source_type, *target_type;
+ const qpol_class_t *target_class;
+ const char *class_name;
+ const qpol_mls_range_t *range;
+ pseudo_range_trans_t *prt = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0, which_pol;
+
+ which_pol = (policy == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD);
+ if (qpol_policy_get_range_trans_iter(q, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if ((v = apol_vector_create(range_trans_free_item)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&qrt) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_range_trans_get_source_type(q, qrt, &source_type) < 0 ||
+ qpol_range_trans_get_target_type(q, qrt, &target_type) < 0 ||
+ qpol_range_trans_get_target_class(q, qrt, &target_class) < 0 ||
+ qpol_class_get_name(q, target_class, &class_name) < 0 || qpol_range_trans_get_range(q, qrt, &range) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (!(prt = calloc(1, sizeof(*prt)))) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ prt->source_type = type_map_lookup(diff, source_type, which_pol);
+ prt->target_type = type_map_lookup(diff, target_type, which_pol);
+ prt->target_class = class_name;
+ prt->range = range;
+ if (apol_vector_append(v, prt)) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ prt = NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort_uniquify(v, pseudo_range_trans_comp, diff);
+ return v;
+
+ err:
+ qpol_iterator_destroy(&iter);
+ apol_vector_destroy(&v);
+ free(prt);
+ errno = error;
+ return NULL;
+}
+
+int range_trans_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const pseudo_range_trans_t *prt1 = x;
+ const pseudo_range_trans_t *prt2 = y;
+ poldiff_range_t *range = NULL;
+ poldiff_range_trans_t *rt = NULL;
+ int error = 0, retval = -1;
+
+ if ((range = range_create(diff, prt1->range, prt2->range, POLDIFF_FORM_MODIFIED)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((retval = range_deep_diff(diff, range)) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (retval > 0) {
+ if ((rt = make_range_trans_diff(diff, POLDIFF_FORM_MODIFIED, prt1)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ rt->range = range;
+ range = NULL;
+ if (apol_vector_append(diff->range_trans_diffs->diffs, rt) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->range_trans_diffs->num_modified++;
+ rt = NULL;
+ }
+ retval = 0;
+ cleanup:
+ range_destroy(&range);
+ range_trans_free(rt);
+ if (retval != 0) {
+ errno = error;
+ }
+ return retval;
+}
diff --git a/libpoldiff/src/range_trans_internal.h b/libpoldiff/src/range_trans_internal.h
new file mode 100644
index 0000000..e1bfe11
--- /dev/null
+++ b/libpoldiff/src/range_trans_internal.h
@@ -0,0 +1,124 @@
+/**
+ * @file
+ * Protected interface for range transition rule differences.
+ *
+ * @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 POLDIFF_RANGE_TRANS_INTERNAL_H
+#define POLDIFF_RANGE_TRANS_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_range_trans_summary poldiff_range_trans_summary_t;
+
+/**
+ * Allocate and return a new poldiff_range_trans_summary_t object.
+ *
+ * @return A new range transition summary. The caller must call
+ * range_trans_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ poldiff_range_trans_summary_t *range_trans_create(void);
+
+/**
+ * Deallocate all space associated with a
+ * poldiff_range_trans_summary_t object, including the pointer itself.
+ * If the pointer is already NULL then do nothing.
+ *
+ * @param rts Reference to a range transition summary to destroy. The
+ * pointer will be set to NULL afterwards.
+ */
+ void range_trans_destroy(poldiff_range_trans_summary_t ** rts);
+
+/**
+ * Reset the state of all range transition rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int range_trans_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all range transition rules from the given policy,
+ * sorted by source type.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of all range transition rules (of
+ * type pseudo_range_trans_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *range_trans_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two pseudo_range_trans_t objects, determining if they have
+ * the same source type, target type, and target class or not.
+ *
+ * @param x The range transition from the original policy.
+ * @param y The range transition from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if x is respectively less than, equal to, or
+ * greater than source role of y.
+ */
+ int range_trans_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a range transition rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int range_trans_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two range transition rules for
+ * which the compare callback returns 0. If a difference is found
+ * then allocate, initialize, and insert a new semantic difference
+ * entry for that range transition rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * rules and to which to add an entry if needed.
+ * @param x The range transition rule from the original policy.
+ * @param y The range transition rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int range_trans_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_RANGE_TRANS_INTERNAL_H */
diff --git a/libpoldiff/src/rbac_diff.c b/libpoldiff/src/rbac_diff.c
new file mode 100644
index 0000000..bc47ba7
--- /dev/null
+++ b/libpoldiff/src/rbac_diff.c
@@ -0,0 +1,1052 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in role allow
+ * rules and role_transition 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
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/bst.h>
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_role_allow_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_role_trans_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ size_t num_added_type;
+ size_t num_removed_type;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_role_allow
+{
+ const char *source_role;
+ poldiff_form_e form;
+ apol_vector_t *orig_roles;
+ apol_vector_t *added_roles;
+ apol_vector_t *removed_roles;
+};
+
+struct poldiff_role_trans
+{
+ const char *source_role;
+ char *target_type;
+ const char *orig_default;
+ const char *mod_default;
+ poldiff_form_e form;
+};
+
+/**************** role allow functions *******************/
+
+void poldiff_role_allow_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->role_allow_diffs->num_added;
+ stats[1] = diff->role_allow_diffs->num_removed;
+ stats[2] = diff->role_allow_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+char *poldiff_role_allow_to_string(const poldiff_t * diff, const void *role_allow)
+{
+ const poldiff_role_allow_t *ra = role_allow;
+ size_t len = 0, i;
+ char *s = NULL, *role;
+ if (diff == NULL || role_allow == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (ra->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ allow %s { ", ra->source_role) < 0) {
+ s = NULL;
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(ra->added_roles); i++) {
+ role = apol_vector_get_element(ra->added_roles, i);
+ if (apol_str_appendf(&s, &len, "%s ", role) < 0) {
+ goto err;
+ }
+ }
+ if (apol_str_append(&s, &len, "};") < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- allow %s { ", ra->source_role) < 0) {
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(ra->removed_roles); i++) {
+ role = apol_vector_get_element(ra->removed_roles, i);
+ if (apol_str_appendf(&s, &len, "%s ", role) < 0) {
+ goto err;
+ }
+ }
+ if (apol_str_append(&s, &len, "};") < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "* allow %s { ", ra->source_role) < 0) {
+ s = NULL;
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(ra->orig_roles); i++) {
+ role = apol_vector_get_element(ra->orig_roles, i);
+ if (apol_str_appendf(&s, &len, "%s ", role) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(ra->added_roles); i++) {
+ role = apol_vector_get_element(ra->added_roles, i);
+ if (apol_str_appendf(&s, &len, "+%s ", role) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(ra->removed_roles); i++) {
+ role = apol_vector_get_element(ra->removed_roles, i);
+ if (apol_str_appendf(&s, &len, "-%s ", role) < 0) {
+ goto err;
+ }
+ }
+ if (apol_str_append(&s, &len, "};") < 0) {
+ break;
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ /* if this is reached then an error occurred */
+ err:
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_role_allow_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->role_allow_diffs->diffs;
+}
+
+const char *poldiff_role_allow_get_name(const poldiff_role_allow_t * role_allow)
+{
+ if (role_allow == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_allow->source_role;
+}
+
+poldiff_form_e poldiff_role_allow_get_form(const void *role_allow)
+{
+ if (role_allow == NULL) {
+ errno = EINVAL;
+ return POLDIFF_FORM_NONE;
+ }
+ return ((const poldiff_role_allow_t *)role_allow)->form;
+}
+
+const apol_vector_t *poldiff_role_allow_get_unmodified_roles(const poldiff_role_allow_t * role_allow)
+{
+ if (role_allow == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_allow->orig_roles;
+}
+
+const apol_vector_t *poldiff_role_allow_get_added_roles(const poldiff_role_allow_t * role_allow)
+{
+ if (role_allow == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_allow->added_roles;
+}
+
+const apol_vector_t *poldiff_role_allow_get_removed_roles(const poldiff_role_allow_t * role_allow)
+{
+ if (role_allow == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_allow->removed_roles;
+}
+
+static void role_allow_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_role_allow_t *r = (poldiff_role_allow_t *) elem;
+ apol_vector_destroy(&r->orig_roles);
+ apol_vector_destroy(&r->added_roles);
+ apol_vector_destroy(&r->removed_roles);
+ free(r);
+ }
+}
+
+poldiff_role_allow_summary_t *role_allow_create(void)
+{
+ poldiff_role_allow_summary_t *ras = calloc(1, sizeof(*ras));
+ if (ras == NULL) {
+ return NULL;
+ }
+ if ((ras->diffs = apol_vector_create(role_allow_free)) == NULL) {
+ role_allow_destroy(&ras);
+ return NULL;
+ }
+ return ras;
+}
+
+void role_allow_destroy(poldiff_role_allow_summary_t ** ras)
+{
+ if (ras != NULL && *ras != NULL) {
+ apol_vector_destroy(&(*ras)->diffs);
+ free(*ras);
+ *ras = NULL;
+ }
+}
+
+typedef struct pseudo_role_allow
+{
+ const char *source_role;
+ apol_vector_t *target_roles;
+} pseudo_role_allow_t;
+
+static void role_allow_free_item(void *item)
+{
+ pseudo_role_allow_t *pra = item;
+
+ if (!item)
+ return;
+
+ /* no need to free source name or target role names */
+ apol_vector_destroy(&pra->target_roles);
+ free(item);
+}
+
+static int role_allow_source_comp(const void *x, const void *y, void *arg __attribute__ ((unused)))
+{
+ const pseudo_role_allow_t *p1 = x;
+ const pseudo_role_allow_t *p2 = y;
+
+ return strcmp(p1->source_role, p2->source_role);
+}
+
+apol_vector_t *role_allow_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *tmp = NULL, *v = NULL;
+ int error = 0, retv;
+ size_t i;
+ apol_bst_t *bst = NULL;
+ pseudo_role_allow_t *pra = NULL;
+ const qpol_role_t *sr = NULL, *tr = NULL;
+ const char *sr_name = NULL, *tr_name = NULL;
+ const qpol_role_allow_t *qra = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+
+ if (qpol_policy_get_role_allow_iter(q, &iter) < 0) {
+ return NULL;
+ }
+
+ tmp = apol_vector_create_from_iter(iter, NULL);
+ if (tmp == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+
+ bst = apol_bst_create(role_allow_source_comp, role_allow_free_item);
+
+ for (i = 0; i < apol_vector_get_size(tmp); i++) {
+ qra = apol_vector_get_element(tmp, i);
+ if (!(pra = calloc(1, sizeof(*pra))) || (!(pra->target_roles = apol_vector_create_with_capacity(1, NULL)))) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_role_allow_get_source_role(q, qra, &sr) || qpol_role_get_name(q, sr, &sr_name)) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ sr = NULL;
+ if (qpol_role_allow_get_target_role(q, qra, &tr) || qpol_role_get_name(q, tr, &tr_name)) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ tr = NULL;
+ pra->source_role = sr_name;
+ retv = apol_bst_insert_and_get(bst, (void **)&pra, NULL);
+ if (retv < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ apol_vector_append_unique(pra->target_roles, (void *)tr_name, apol_str_strcmp, NULL);
+ pra = NULL;
+ }
+ apol_vector_destroy(&tmp);
+
+ v = apol_bst_get_vector(bst, 1);
+ if (!v) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ apol_bst_destroy(&bst);
+
+ return v;
+
+ err:
+ role_allow_free_item(pra);
+ apol_bst_destroy(&bst);
+ errno = error;
+ return NULL;
+}
+
+int role_allow_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused)))
+{
+ const pseudo_role_allow_t *p1 = x;
+ const pseudo_role_allow_t *p2 = y;
+
+ return strcmp(p1->source_role, p2->source_role);
+}
+
+int role_allow_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ role_allow_destroy(&diff->role_allow_diffs);
+ diff->role_allow_diffs = role_allow_create();
+ if (diff->role_allow_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Allocate and return a new role allow rule difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param source_role Name of the source role in the role allow rule.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling role_allow_free() upon the returned
+ * value.
+ */
+static poldiff_role_allow_t *make_ra_diff(const poldiff_t * diff, poldiff_form_e form, const char *source_role)
+{
+ poldiff_role_allow_t *ra = NULL;
+ int error = 0;
+ if ((ra = calloc(1, sizeof(*ra))) == NULL ||
+ (ra->source_role = source_role) == NULL ||
+ (ra->added_roles = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ (ra->orig_roles = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ (ra->removed_roles = apol_vector_create_with_capacity(1, NULL)) == NULL) {
+ error = errno;
+ role_allow_free(ra);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ ra->form = form;
+ return ra;
+}
+
+int role_allow_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ pseudo_role_allow_t *ra = (pseudo_role_allow_t *) item;
+ poldiff_role_allow_t *pra;
+ int error;
+
+ pra = make_ra_diff(diff, form, ra->source_role);
+ if (pra == NULL) {
+ return -1;
+ }
+ int rt;
+ if (form == POLDIFF_FORM_ADDED) {
+ rt = apol_vector_cat(pra->added_roles, ra->target_roles);
+ } else {
+ rt = apol_vector_cat(pra->removed_roles, ra->target_roles);
+ }
+ if (rt < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ role_allow_free(pra);
+ errno = error;
+ return -1;
+ }
+ if (apol_vector_append(diff->role_allow_diffs->diffs, pra) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ role_allow_free(pra);
+ errno = error;
+ return -1;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->role_allow_diffs->num_added++;
+ } else {
+ diff->role_allow_diffs->num_removed++;
+ }
+ return 0;
+}
+
+int role_allow_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const pseudo_role_allow_t *p1 = x;
+ const pseudo_role_allow_t *p2 = y;
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ char *role1, *role2;
+ poldiff_role_allow_t *pra = NULL;
+ size_t i, j;
+ int retval = -1, error = 0, compval;
+
+ v1 = p1->target_roles;
+ v2 = p2->target_roles;
+
+ apol_vector_sort(v1, apol_str_strcmp, NULL);
+ apol_vector_sort(v2, apol_str_strcmp, NULL);
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2))
+ break;
+ role1 = (char *)apol_vector_get_element(v1, i);
+ role2 = (char *)apol_vector_get_element(v2, j);
+ compval = strcmp(role1, role2);
+ if (pra == NULL) {
+ if ((pra = make_ra_diff(diff, POLDIFF_FORM_MODIFIED, p1->source_role)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if (compval < 0) {
+ if (apol_vector_append(pra->removed_roles, role1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (compval > 0) {
+ if (apol_vector_append(pra->added_roles, role2) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ if (apol_vector_append(pra->orig_roles, role1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ role1 = (char *)apol_vector_get_element(v1, i);
+ if (pra == NULL) {
+ if ((pra = make_ra_diff(diff, POLDIFF_FORM_MODIFIED, p1->source_role)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if (apol_vector_append(pra->removed_roles, role1) < 0) {
+ error = errno;
+ free(role1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ role2 = (char *)apol_vector_get_element(v2, j);
+ if (pra == NULL) {
+ if ((pra = make_ra_diff(diff, POLDIFF_FORM_MODIFIED, p1->source_role)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if (apol_vector_append(pra->added_roles, role2) < 0) {
+ error = errno;
+ free(role2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_get_size(pra->added_roles) || apol_vector_get_size(pra->removed_roles)) {
+ apol_vector_sort(pra->removed_roles, apol_str_strcmp, NULL);
+ apol_vector_sort(pra->added_roles, apol_str_strcmp, NULL);
+ apol_vector_sort(pra->orig_roles, apol_str_strcmp, NULL);
+ if (apol_vector_append(diff->role_allow_diffs->diffs, pra) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->role_allow_diffs->num_modified++;
+ } else {
+ role_allow_free(pra);
+ pra = NULL;
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ role_allow_free(pra);
+ }
+ errno = error;
+ return retval;
+}
+
+/**************** role_transition functions *******************/
+
+void poldiff_role_trans_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->role_trans_diffs->num_added;
+ stats[1] = diff->role_trans_diffs->num_removed;
+ stats[2] = diff->role_trans_diffs->num_modified;
+ stats[3] = diff->role_trans_diffs->num_added_type;
+ stats[4] = diff->role_trans_diffs->num_removed_type;
+}
+
+extern char *poldiff_role_trans_to_string(const poldiff_t * diff, const void *role_trans)
+{
+ const poldiff_role_trans_t *rt = role_trans;
+ char *s = NULL;
+
+ if (diff == NULL || role_trans == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (rt->form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ if (asprintf(&s, "+ role_transition %s %s %s;", rt->source_role, rt->target_type, rt->mod_default) < 0)
+ break;
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ if (asprintf(&s, "- role_transition %s %s %s;", rt->source_role, rt->target_type, rt->orig_default) < 0)
+ break;
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (asprintf
+ (&s, "* role_transition %s %s { +%s -%s };", rt->source_role, rt->target_type, rt->mod_default,
+ rt->orig_default) < 0)
+ break;
+ return s;
+ }
+ case POLDIFF_FORM_NONE:
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_role_trans_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->role_trans_diffs->diffs;
+}
+
+extern const char *poldiff_role_trans_get_source_role(const poldiff_role_trans_t * role_trans)
+{
+ if (role_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_trans->source_role;
+}
+
+extern const char *poldiff_role_trans_get_target_type(const poldiff_role_trans_t * role_trans)
+{
+ if (role_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_trans->target_type;
+}
+
+extern poldiff_form_e poldiff_role_trans_get_form(const void *role_trans)
+{
+ if (role_trans == NULL) {
+ errno = EINVAL;
+ return POLDIFF_FORM_NONE;
+ }
+ return ((const poldiff_role_trans_t *)role_trans)->form;
+}
+
+extern const char *poldiff_role_trans_get_original_default(const poldiff_role_trans_t * role_trans)
+{
+ if (role_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_trans->orig_default;
+}
+
+extern const char *poldiff_role_trans_get_modified_default(const poldiff_role_trans_t * role_trans)
+{
+ if (role_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_trans->mod_default;
+}
+
+static void role_trans_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_role_trans_t *rt = elem;
+ free(rt->target_type);
+ free(rt);
+ }
+}
+
+poldiff_role_trans_summary_t *role_trans_create(void)
+{
+ poldiff_role_trans_summary_t *rts = calloc(1, sizeof(*rts));
+ if (rts == NULL) {
+ return NULL;
+ }
+ if ((rts->diffs = apol_vector_create(role_trans_free)) == NULL) {
+ role_trans_destroy(&rts);
+ return NULL;
+ }
+ return rts;
+}
+
+void role_trans_destroy(poldiff_role_trans_summary_t ** rts)
+{
+ if (rts != NULL && *rts != NULL) {
+ apol_vector_destroy(&(*rts)->diffs);
+ free(*rts);
+ *rts = NULL;
+ }
+}
+
+int role_trans_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ role_trans_destroy(&diff->role_trans_diffs);
+ diff->role_trans_diffs = role_trans_create();
+ if (diff->role_trans_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+typedef struct pseudo_role_trans
+{
+ const char *source_role;
+ uint32_t pseudo_target;
+ const char *default_role;
+} pseudo_role_trans_t;
+
+/**
+ * Compare two pseudo role_transition rules from the same policy.
+ * Compares the source role name and then pseudo type value of the target.
+ *
+ * @param x A pseudo_role_trans_t entry.
+ * @param y A pseudo_role_trans_t entry.
+ * @param arg The policy difference structure.
+ *
+ * @return < 0, 0, or > 0 if the first rule is respectively less than,
+ * equal to, or greater than the second. If the return value would be 0
+ * but the default role is different a warning is issued.
+ */
+static int pseudo_role_trans_comp(const void *x, const void *y, void *arg)
+{
+ int retv = 0;
+ const pseudo_role_trans_t *a = x;
+ const pseudo_role_trans_t *b = y;
+ poldiff_t *diff = arg;
+
+ retv = strcmp(a->source_role, b->source_role);
+ if (!retv)
+ retv = a->pseudo_target - b->pseudo_target;
+ else
+ return retv;
+ if (!retv && strcmp(a->default_role, b->default_role))
+ WARN(diff, "Multiple role_transition rules for %s %s with different default roles.", a->source_role,
+ type_map_get_name(diff, a->pseudo_target, POLDIFF_POLICY_ORIG));
+ return retv;
+}
+
+static void role_trans_free_item(void *item)
+{
+ /* no need to free members of a pseudo role_transition */
+ free(item);
+}
+
+apol_vector_t *role_trans_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL, *attr_types = NULL;
+ apol_vector_t *v = NULL;
+ const qpol_role_trans_t *qrt = NULL;
+ pseudo_role_trans_t *tmp_prt = NULL;
+ const char *tmp_name = NULL;
+ const qpol_role_t *tmp_role = NULL;
+ const qpol_type_t *tmp_type = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0, which_pol;
+ unsigned char isattr = 0;
+
+ which_pol = (policy == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD);
+ if (qpol_policy_get_role_trans_iter(q, &iter)) {
+ error = errno;
+ goto err;
+ }
+ v = apol_vector_create(role_trans_free_item);
+ if (!v) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ isattr = 0;
+ if (qpol_iterator_get_item(iter, (void **)&qrt) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_role_trans_get_target_type(q, qrt, &tmp_type) < 0) {
+ error = errno;
+ goto err;
+ }
+ qpol_type_get_isattr(q, tmp_type, &isattr);
+ if (isattr) {
+ qpol_type_get_type_iter(q, tmp_type, &attr_types);
+ for (; !qpol_iterator_end(attr_types); qpol_iterator_next(attr_types)) {
+ qpol_iterator_get_item(attr_types, (void **)&tmp_type);
+ if (!(tmp_prt = calloc(1, sizeof(*tmp_prt)))) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ tmp_prt->pseudo_target = type_map_lookup(diff, tmp_type, which_pol);
+ qpol_role_trans_get_source_role(q, qrt, &tmp_role);
+ qpol_role_get_name(q, tmp_role, &tmp_name);
+ tmp_prt->source_role = tmp_name;
+ qpol_role_trans_get_default_role(q, qrt, &tmp_role);
+ qpol_role_get_name(q, tmp_role, &tmp_name);
+ tmp_prt->default_role = tmp_name;
+ if (apol_vector_append(v, tmp_prt)) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ tmp_prt = NULL;
+ }
+ qpol_iterator_destroy(&attr_types);
+ } else {
+ if (!(tmp_prt = calloc(1, sizeof(*tmp_prt)))) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ tmp_prt->pseudo_target = type_map_lookup(diff, tmp_type, which_pol);
+ qpol_role_trans_get_source_role(q, qrt, &tmp_role);
+ qpol_role_get_name(q, tmp_role, &tmp_name);
+ tmp_prt->source_role = tmp_name;
+ qpol_role_trans_get_default_role(q, qrt, &tmp_role);
+ qpol_role_get_name(q, tmp_role, &tmp_name);
+ tmp_prt->default_role = tmp_name;
+ if (apol_vector_append(v, tmp_prt)) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ tmp_prt = NULL;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort_uniquify(v, pseudo_role_trans_comp, diff);
+
+ return v;
+
+ err:
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&attr_types);
+ apol_vector_destroy(&v);
+ free(tmp_prt);
+ errno = error;
+ return NULL;
+}
+
+int role_trans_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused)))
+{
+ int retv = 0;
+ const pseudo_role_trans_t *a = x;
+ const pseudo_role_trans_t *b = y;
+
+ retv = strcmp(a->source_role, b->source_role);
+ if (!retv)
+ return a->pseudo_target - b->pseudo_target;
+ else
+ return retv;
+}
+
+/**
+ * Allocate and return a new role_transition rule difference object.
+ *
+ * @param diff Policy difference error handler.
+ * @param form Form of the difference.
+ * @param src Name of the source role.
+ * @param tgt Name of the target type.
+ *
+ * @return A newly allocated and initialised diff or NULL upon error.
+ * The caller is responsible for calling free() upon the returned
+ * value.
+ */
+static poldiff_role_trans_t *make_rt_diff(const poldiff_t * diff, poldiff_form_e form, const char *src, const char *tgt)
+{
+ poldiff_role_trans_t *rt = NULL;
+ int error = 0;
+ if ((rt = calloc(1, sizeof(*rt))) == NULL || (rt->source_role = src) == NULL || (rt->target_type = strdup(tgt)) == NULL) {
+ error = errno;
+ role_trans_free(rt);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ rt->form = form;
+ return rt;
+}
+
+int role_trans_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const pseudo_role_trans_t *rt = item;
+ poldiff_role_trans_t *prt = NULL;
+ const char *tgt_name = NULL;
+ int error = 0;
+
+ /* get tgt_name from type_map */
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ tgt_name = type_map_get_name(diff, rt->pseudo_target, POLDIFF_POLICY_MOD);
+ if (type_map_get_name(diff, rt->pseudo_target, POLDIFF_POLICY_ORIG) == NULL) {
+ form = POLDIFF_FORM_ADD_TYPE;
+ }
+ break;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ tgt_name = type_map_get_name(diff, rt->pseudo_target, POLDIFF_POLICY_ORIG);
+ if (type_map_get_name(diff, rt->pseudo_target, POLDIFF_POLICY_MOD) == NULL) {
+ form = POLDIFF_FORM_REMOVE_TYPE;
+ }
+ break;
+ }
+ case POLDIFF_FORM_MODIFIED: /* not supported here */
+ case POLDIFF_FORM_NONE:
+ default:
+ {
+ assert(0);
+ return -1;
+ }
+ }
+ assert(tgt_name != NULL);
+
+ /* create a new diff */
+ prt = make_rt_diff(diff, form, rt->source_role, tgt_name);
+ if (!prt)
+ return -1;
+
+ /* set the appropriate default */
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ prt->mod_default = rt->default_role;
+ break;
+ }
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ prt->orig_default = rt->default_role;
+ break;
+ }
+ default:
+ {
+ /* not reachable */
+ assert(0);
+ }
+ }
+ if (apol_vector_append(diff->role_trans_diffs->diffs, prt)) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ role_trans_free(prt);
+ errno = error;
+ return -1;
+ };
+
+ /* increment appropriate counter */
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ diff->role_trans_diffs->num_added++;
+ break;
+ }
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ diff->role_trans_diffs->num_added_type++;
+ break;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ diff->role_trans_diffs->num_removed++;
+ break;
+ }
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ diff->role_trans_diffs->num_removed_type++;
+ break;
+ }
+ default:
+ {
+ /* not reachable */
+ assert(0);
+ }
+ }
+
+ return 0;
+}
+
+int role_trans_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const pseudo_role_trans_t *prt1 = x;
+ const pseudo_role_trans_t *prt2 = y;
+ const char *default1 = NULL, *default2 = NULL;
+ poldiff_role_trans_t *rt = NULL;
+ const char *tgt = NULL;
+ int error = 0;
+
+ default1 = prt1->default_role;
+ default2 = prt2->default_role;
+
+ if (!strcmp(default1, default2))
+ return 0; /* no difference */
+
+ tgt = type_map_get_name(diff, prt1->pseudo_target, POLDIFF_POLICY_ORIG);
+ assert(tgt != NULL);
+ rt = make_rt_diff(diff, POLDIFF_FORM_MODIFIED, prt1->source_role, tgt);
+ if (!rt)
+ return -1; /* errors already reported */
+ rt->orig_default = default1;
+ rt->mod_default = default2;
+ if (apol_vector_append(diff->role_trans_diffs->diffs, rt)) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ role_trans_free(rt);
+ errno = error;
+ return -1;
+ };
+ diff->role_trans_diffs->num_modified++;
+
+ return 0;
+}
diff --git a/libpoldiff/src/rbac_internal.h b/libpoldiff/src/rbac_internal.h
new file mode 100644
index 0000000..6c89234
--- /dev/null
+++ b/libpoldiff/src/rbac_internal.h
@@ -0,0 +1,209 @@
+/**
+ * @file
+ * Protected interface for role allow rule and role_transition rule
+ * differences.
+ *
+ * @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 POLDIFF_RBAC_INTERNAL_H
+#define POLDIFF_RBAC_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_role_allow_summary poldiff_role_allow_summary_t;
+ typedef struct poldiff_role_trans_summary poldiff_role_trans_summary_t;
+
+/**
+ * Allocate and return a new poldiff_role_allow_summary_t object.
+ *
+ * @return A new role allow summary. The caller must call role_allow_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_role_allow_summary_t *role_allow_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_role_allow_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param ras Reference to a role allow summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void role_allow_destroy(poldiff_role_allow_summary_t ** ras);
+
+/**
+ * Reset the state of all role allow rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int role_allow_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all role allow rules from the given policy,
+ * sorted by source name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of all role allow rules (of type
+ * pseudo_role_allow_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *role_allow_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two pseudo_role_allow_t objects, determining if they have the same
+ * source name or not.
+ *
+ * @param x The role allow from the original policy.
+ * @param y The role allow from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if source role of x is respectively less than, equal
+ * to, or greater than source role of y.
+ */
+ int role_allow_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a role allow rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int role_allow_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two role allow rules for which the
+ * compare callback returns 0. If a difference is found then allocate,
+ * initialize, and insert a new semantic difference entry for that role allow
+ * rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * rules and to which to add an entry if needed.
+ * @param x The role allow rule from the original policy.
+ * @param y The role allow rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int role_allow_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Allocate and return a new poldiff_role_trans_summary_t object.
+ *
+ * @return A new role transition summary. The caller must call
+ * role_trans_destroy() afterwards. On error, return NULL and set errno.
+ */
+ poldiff_role_trans_summary_t *role_trans_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_role_trans_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param rts Reference to a role transition summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void role_trans_destroy(poldiff_role_trans_summary_t ** rts);
+
+/**
+ * Reset the state of all role_transition rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int role_trans_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all role_transition rules from the given policy,
+ * sorted by source name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of all role_transition rules (of
+ * type pseudo_role_trans_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *role_trans_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two pseudo_role_trans_t objects, determining if they have the same
+ * source name and target or not.
+ *
+ * @param x The role_transition from the original policy.
+ * @param y The role_transition from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if source role of x is respectively less than, equal
+ * to, or greater than source role of y.
+ */
+ int role_trans_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a role_transition rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int role_trans_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two role_transition rules for which the
+ * compare callback returns 0. If a difference is found then allocate,
+ * initialize, and insert a new semantic difference entry for that
+ * role_transition rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * rules and to which to add an entry if needed.
+ * @param x The role_transition rule from the original policy.
+ * @param y The role_transition rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int role_trans_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_RBAC_INTERNAL_H */
diff --git a/libpoldiff/src/role_diff.c b/libpoldiff/src/role_diff.c
new file mode 100644
index 0000000..bac2062
--- /dev/null
+++ b/libpoldiff/src/role_diff.c
@@ -0,0 +1,543 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in roles.
+ *
+ * @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 "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_role_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_role
+{
+ char *name;
+ poldiff_form_e form;
+ apol_vector_t *added_types;
+ apol_vector_t *removed_types;
+};
+
+void poldiff_role_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->role_diffs->num_added;
+ stats[1] = diff->role_diffs->num_removed;
+ stats[2] = diff->role_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+char *poldiff_role_to_string(const poldiff_t * diff, const void *role)
+{
+ const poldiff_role_t *r = role;
+ size_t num_added, num_removed, len = 0, i;
+ char *s = NULL, *type;
+ if (diff == NULL || role == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ num_added = apol_vector_get_size(r->added_types);
+ num_removed = apol_vector_get_size(r->removed_types);
+ switch (r->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", r->name) < 0) {
+ s = NULL;
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", r->name) < 0) {
+ s = NULL;
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "* %s (", r->name) < 0) {
+ s = NULL;
+ break;
+ }
+ if (num_added > 0) {
+ if (apol_str_appendf(&s, &len, "%zd Added Type%s", num_added, (num_added == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (num_removed > 0) {
+ if (apol_str_appendf
+ (&s, &len, "%s%zd Removed Type%s", (num_added > 0 ? ", " : ""), num_removed,
+ (num_removed == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (apol_str_append(&s, &len, ")\n") < 0) {
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(r->added_types); i++) {
+ type = (char *)apol_vector_get_element(r->added_types, i);
+ if (apol_str_appendf(&s, &len, "\t+ %s\n", type) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(r->removed_types); i++) {
+ type = (char *)apol_vector_get_element(r->removed_types, i);
+ if (apol_str_appendf(&s, &len, "\t- %s\n", type) < 0) {
+ goto err;
+ }
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ err:
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_role_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->role_diffs->diffs;
+}
+
+const char *poldiff_role_get_name(const poldiff_role_t * role)
+{
+ if (role == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role->name;
+}
+
+poldiff_form_e poldiff_role_get_form(const void *role)
+{
+ if (role == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_role_t *)role)->form;
+}
+
+const apol_vector_t *poldiff_role_get_added_types(const poldiff_role_t * role)
+{
+ if (role == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role->added_types;
+}
+
+const apol_vector_t *poldiff_role_get_removed_types(const poldiff_role_t * role)
+{
+ if (role == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role->removed_types;
+}
+
+/*************** protected functions for roles ***************/
+
+static void role_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_role_t *r = (poldiff_role_t *) elem;
+ free(r->name);
+ apol_vector_destroy(&r->added_types);
+ apol_vector_destroy(&r->removed_types);
+ free(r);
+ }
+}
+
+poldiff_role_summary_t *role_create(void)
+{
+ poldiff_role_summary_t *rs = calloc(1, sizeof(*rs));
+ if (rs == NULL) {
+ return NULL;
+ }
+ if ((rs->diffs = apol_vector_create(role_free)) == NULL) {
+ role_destroy(&rs);
+ return NULL;
+ }
+ return rs;
+}
+
+void role_destroy(poldiff_role_summary_t ** rs)
+{
+ if (rs != NULL && *rs != NULL) {
+ apol_vector_destroy(&(*rs)->diffs);
+ free(*rs);
+ *rs = NULL;
+ }
+}
+
+int role_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ role_destroy(&diff->role_diffs);
+ diff->role_diffs = role_create();
+ if (diff->role_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two roles from the same policy.
+ */
+static int role_name_comp(const void *x, const void *y, void *arg)
+{
+ const qpol_role_t *r1 = x;
+ const qpol_role_t *r2 = y;
+ apol_policy_t *p = (apol_policy_t *) arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+ if (qpol_role_get_name(q, r1, &name1) < 0 || qpol_role_get_name(q, r2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *role_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_role_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create_from_iter(iter, NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, role_name_comp, (void *)policy);
+ return v;
+}
+
+int role_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ const qpol_role_t *r1 = x;
+ const qpol_role_t *r2 = y;
+ const char *name1, *name2;
+ if (qpol_role_get_name(diff->orig_qpol, r1, &name1) < 0 || qpol_role_get_name(diff->mod_qpol, r2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new role difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the role that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling role_free() upon the returned
+ * value.
+ */
+static poldiff_role_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_role_t *pr;
+ int error;
+ if ((pr = calloc(1, sizeof(*pr))) == NULL ||
+ (pr->name = strdup(name)) == NULL ||
+ (pr->added_types = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pr->removed_types = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = errno;
+ role_free(pr);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pr->form = form;
+ return pr;
+}
+
+int role_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const qpol_role_t *r = item;
+ const char *name = NULL;
+ poldiff_role_t *pr;
+ int error;
+ if ((form == POLDIFF_FORM_ADDED &&
+ qpol_role_get_name(diff->mod_qpol, r, &name) < 0) ||
+ ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_role_get_name(diff->orig_qpol, r, &name) < 0))
+ {
+ return -1;
+ }
+ pr = make_diff(diff, form, name);
+ if (pr == NULL) {
+ return -1;
+ }
+ if (apol_vector_append(diff->role_diffs->diffs, pr) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ role_free(pr);
+ errno = error;
+ return -1;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->role_diffs->num_added++;
+ } else {
+ diff->role_diffs->num_removed++;
+ }
+ return 0;
+}
+
+/**
+ * Given a role, return an unsorted vector of its allowed types (in
+ * the form of uint32_t corresponding to pseudo-type values).
+ *
+ * @param diff Policy diff error handler.
+ * @param role Role whose roles to get.
+ * @param which Which policy, one of POLDIFF_POLICY_ORIG or
+ * POLDIFF_POLICY_MOD.
+ *
+ * @return Vector of pseudo-type values. The caller is responsible
+ * for calling apol_vector_destroy(). On error, return NULL.
+ */
+static apol_vector_t *role_get_types(const poldiff_t * diff, const qpol_role_t * role, int which)
+{
+ qpol_iterator_t *iter = NULL;
+ const qpol_type_t *type;
+ uint32_t new_val;
+ apol_vector_t *v = NULL;
+ int retval = -1, error = 0;
+
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (which == POLDIFF_POLICY_ORIG) {
+ if (qpol_role_get_type_iter(diff->orig_qpol, role, &iter) < 0) {
+ goto cleanup;
+ }
+ } else {
+ if (qpol_role_get_type_iter(diff->mod_qpol, role, &iter) < 0) {
+ goto cleanup;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0 || (new_val = type_map_lookup(diff, type, which)) == 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_append(v, (void *)((size_t) new_val)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+int role_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const qpol_role_t *r1 = x;
+ const qpol_role_t *r2 = y;
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ apol_vector_t *added_types = NULL, *removed_types = NULL;
+ const apol_vector_t *reverse_v;
+ const char *name;
+ char *new_name;
+ uint32_t t1, t2;
+ poldiff_role_t *r = NULL;
+ qpol_type_t *t;
+ size_t i, j;
+ int retval = -1, error = 0;
+
+ if (qpol_role_get_name(diff->orig_qpol, r1, &name) < 0 ||
+ (v1 = role_get_types(diff, r1, POLDIFF_POLICY_ORIG)) == NULL ||
+ (v2 = role_get_types(diff, r2, POLDIFF_POLICY_MOD)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_sort_uniquify(v1, NULL, NULL);
+ apol_vector_sort_uniquify(v2, NULL, NULL);
+ if ((added_types = apol_vector_create(NULL)) == NULL || (removed_types = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2))
+ break;
+ t1 = (uint32_t) ((size_t) apol_vector_get_element(v1, i));
+ t2 = (uint32_t) ((size_t) apol_vector_get_element(v2, j));
+ if (t2 > t1) {
+ if (apol_vector_append(removed_types, (void *)((size_t) t1)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (t1 > t2) {
+ if (apol_vector_append(added_types, (void *)((size_t) t2)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ t1 = (uint32_t) ((size_t) apol_vector_get_element(v1, i));
+ if (apol_vector_append(removed_types, (void *)((size_t) t1)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ t2 = (uint32_t) ((size_t) apol_vector_get_element(v2, j));
+ if (apol_vector_append(added_types, (void *)((size_t) t2)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_get_size(added_types) > 0 || apol_vector_get_size(removed_types) > 0) {
+ if ((r = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(removed_types); i++) {
+ t1 = (uint32_t) ((size_t) apol_vector_get_element(removed_types, i));
+ if ((reverse_v = type_map_lookup_reverse(diff, t1, POLDIFF_POLICY_ORIG)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (j = 0; j < apol_vector_get_size(reverse_v); j++) {
+ t = (qpol_type_t *) apol_vector_get_element(reverse_v, j);
+ if (qpol_type_get_name(diff->orig_qpol, t, &name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((new_name = strdup(name)) == NULL || apol_vector_append(r->removed_types, new_name) < 0) {
+ error = errno;
+ free(new_name);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(added_types); i++) {
+ t2 = (uint32_t) ((size_t) apol_vector_get_element(added_types, i));
+ if ((reverse_v = type_map_lookup_reverse(diff, t2, POLDIFF_POLICY_MOD)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (j = 0; j < apol_vector_get_size(reverse_v); j++) {
+ t = (qpol_type_t *) apol_vector_get_element(reverse_v, j);
+ if (qpol_type_get_name(diff->mod_qpol, t, &name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((new_name = strdup(name)) == NULL || apol_vector_append(r->added_types, new_name) < 0) {
+ error = errno;
+ free(new_name);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ }
+ apol_vector_sort(r->removed_types, apol_str_strcmp, NULL);
+ apol_vector_sort(r->added_types, apol_str_strcmp, NULL);
+ if (apol_vector_append(diff->role_diffs->diffs, r) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->role_diffs->num_modified++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ apol_vector_destroy(&added_types);
+ apol_vector_destroy(&removed_types);
+ if (retval != 0) {
+ role_free(r);
+ }
+ errno = error;
+ return retval;
+}
diff --git a/libpoldiff/src/role_internal.h b/libpoldiff/src/role_internal.h
new file mode 100644
index 0000000..7c2aa90
--- /dev/null
+++ b/libpoldiff/src/role_internal.h
@@ -0,0 +1,121 @@
+/**
+ * @file
+ * Protected interface for role differences.
+ *
+ * @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 POLDIFF_ROLE_INTERNAL_H
+#define POLDIFF_ROLE_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_role_summary poldiff_role_summary_t;
+
+/**
+ * Allocate and return a new poldiff_role_summary_t object.
+ *
+ * @return A new role summary. The caller must call role_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_role_summary_t *role_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_role_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param us Reference to a role summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void role_destroy(poldiff_role_summary_t ** us);
+
+/**
+ * Reset the state of all role differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int role_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all roles from the given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all roles. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *role_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_role_t objects, determining if they have the same
+ * name or not.
+ *
+ * @param x The role from the original policy.
+ * @param y The role from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if role x is respectively less than, equal
+ * to, or greater than role y.
+ */
+ int role_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a role.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int role_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two roles for which the compare
+ * callback returns 0. If a difference is found then allocate,
+ * initialize, and insert a new semantic difference entry for that
+ * role.
+ *
+ * @param diff The policy difference structure associated with both
+ * roles and to which to add an entry if needed.
+ * @param x The role from the original policy.
+ * @param y The role from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int role_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_ROLE_INTERNAL_H */
diff --git a/libpoldiff/src/terule_diff.c b/libpoldiff/src/terule_diff.c
new file mode 100644
index 0000000..2857403
--- /dev/null
+++ b/libpoldiff/src/terule_diff.c
@@ -0,0 +1,1329 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in AV and Type
+ * 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
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/policy-query.h>
+#include <apol/util.h>
+#include <qpol/policy_extend.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_terule_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ size_t num_added_type;
+ size_t num_removed_type;
+ int diffs_sorted;
+ /** vector of poldiff_terule_t */
+ apol_vector_t *diffs;
+};
+
+struct poldiff_terule
+{
+ uint32_t spec;
+ /* pointer into policy's symbol table */
+ const char *source, *target;
+ /** the class string is pointer into the class_bst BST */
+ const char *cls;
+ poldiff_form_e form;
+ /* pointer into policy's symbol table */
+ const char *orig_default, *mod_default;
+ /** pointer into policy's conditional list, needed to render
+ * conditional expressions */
+ const qpol_cond_t *cond;
+ uint32_t branch;
+ /** vector of unsigned longs of line numbers from original policy */
+ apol_vector_t *orig_linenos;
+ /** vector of unsigned longs of line numbers from modified policy */
+ apol_vector_t *mod_linenos;
+ /** array of pointers for original rules */
+ qpol_terule_t **orig_rules;
+ size_t num_orig_rules;
+ /** array of pointers for modified rules */
+ qpol_terule_t **mod_rules;
+ size_t num_mod_rules;
+};
+
+typedef struct pseudo_terule
+{
+ uint32_t spec;
+ /** pseudo-type values */
+ uint32_t source, target, default_type;
+ /** pointer into the class_bst BST */
+ const char *cls;
+ /** array of pointers into the bool_bst BST */
+ const char *bools[5];
+ uint32_t bool_val;
+ uint32_t branch;
+ /** pointer into policy's conditional list, needed to render
+ * conditional expressions */
+ const qpol_cond_t *cond;
+ /** array of qpol_terule_t pointers, for showing line numbers */
+ const qpol_terule_t **rules;
+ size_t num_rules;
+} pseudo_terule_t;
+
+/******************** public terule functions ********************/
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for te rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ * @param idx Index into the terule diffs array indicating which rule
+ * type to reset, one of TERULE_OFFSET_MEMBER, etc.
+ */
+static void poldiff_terule_get_stats(const poldiff_t * diff, size_t stats[5], unsigned int idx)
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->terule_diffs[idx]->num_added;
+ stats[1] = diff->terule_diffs[idx]->num_removed;
+ stats[2] = diff->terule_diffs[idx]->num_modified;
+ stats[3] = diff->terule_diffs[idx]->num_added_type;
+ stats[4] = diff->terule_diffs[idx]->num_removed_type;
+}
+
+void poldiff_terule_get_stats_change(const poldiff_t * diff, size_t stats[5])
+{
+ poldiff_terule_get_stats(diff, stats, TERULE_OFFSET_CHANGE);
+}
+
+void poldiff_terule_get_stats_member(const poldiff_t * diff, size_t stats[5])
+{
+ poldiff_terule_get_stats(diff, stats, TERULE_OFFSET_MEMBER);
+}
+
+void poldiff_terule_get_stats_trans(const poldiff_t * diff, size_t stats[5])
+{
+ poldiff_terule_get_stats(diff, stats, TERULE_OFFSET_TRANS);
+}
+
+char *poldiff_terule_to_string(const poldiff_t * diff, const void *terule)
+{
+ const poldiff_terule_t *pt = (const poldiff_terule_t *)terule;
+ apol_policy_t *p;
+ const char *rule_type;
+ char *diff_char = "", *s = NULL, *cond_expr = NULL;
+ size_t len = 0;
+ int error;
+ if (diff == NULL || terule == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (pt->form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ diff_char = "+";
+ p = diff->mod_pol;
+ break;
+ }
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ diff_char = "-";
+ p = diff->orig_pol;
+ break;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ diff_char = "*";
+ p = diff->orig_pol;
+ break;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ rule_type = apol_rule_type_to_str(pt->spec);
+ if (apol_str_appendf(&s, &len, "%s %s %s %s : %s ", diff_char, rule_type, pt->source, pt->target, pt->cls) < 0) {
+ error = errno;
+ s = NULL;
+ goto err;
+ }
+ switch (pt->form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ if (apol_str_append(&s, &len, pt->mod_default) < 0) {
+ error = errno;
+ goto err;
+ }
+ break;
+ }
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ if (apol_str_append(&s, &len, pt->orig_default) < 0) {
+ error = errno;
+ goto err;
+ }
+ break;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "{ -%s +%s }", pt->orig_default, pt->mod_default) < 0) {
+ error = errno;
+ goto err;
+ }
+ break;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ if (apol_str_append(&s, &len, ";") < 0) {
+ error = errno;
+ goto err;
+ }
+ if (pt->cond != NULL) {
+ if ((cond_expr = apol_cond_expr_render(p, pt->cond)) == NULL) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&s, &len, " [%s]:%s", cond_expr, (pt->branch ? "TRUE" : "FALSE")) < 0) {
+ error = errno;
+ goto err;
+ }
+ free(cond_expr);
+ }
+ return s;
+ err:
+ free(s);
+ free(cond_expr);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+}
+
+/**
+ * Sort poldiff_terule diff results in a mostly alphabetical order.
+ */
+static int poldiff_terule_cmp(const void *x, const void *y, void *data __attribute__ ((unused)))
+{
+ const poldiff_terule_t *a = (const poldiff_terule_t *)x;
+ const poldiff_terule_t *b = (const poldiff_terule_t *)y;
+ int compval;
+ if (a->spec != b->spec) {
+ const char *rule_type1 = apol_rule_type_to_str(a->spec);
+ const char *rule_type2 = apol_rule_type_to_str(b->spec);
+ compval = strcmp(rule_type1, rule_type2);
+ if (compval != 0) {
+ return compval;
+ }
+ }
+ if ((compval = strcmp(a->source, b->source)) != 0) {
+ return compval;
+ }
+ if ((compval = strcmp(a->target, b->target)) != 0) {
+ return compval;
+ }
+ if ((compval = strcmp(a->cls, b->cls)) != 0) {
+ return compval;
+ }
+ if (a->cond != b->cond) {
+ return (int)((char *)a->cond - (char *)b->cond);
+ }
+ /* sort true branch before false branch */
+ return b->branch - a->branch;
+}
+
+/**
+ * Get the vector of te rule differences from the te rule difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the te
+ * rule difference summary.
+ * @param idx Index into the terule diffs array indicating which rule
+ * type to reset, one of TERULE_OFFSET_MEMBER, etc.
+ *
+ * @return A vector of elements of type poldiff_terule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+static const apol_vector_t *poldiff_get_terule_vector(const poldiff_t * diff, terule_offset_e idx)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (diff->terule_diffs[idx]->diffs_sorted == 0) {
+ apol_vector_sort(diff->terule_diffs[idx]->diffs, poldiff_terule_cmp, NULL);
+ diff->terule_diffs[idx]->diffs_sorted = 1;
+ }
+ return diff->terule_diffs[idx]->diffs;
+}
+
+const apol_vector_t *poldiff_get_terule_vector_member(const poldiff_t * diff)
+{
+ return poldiff_get_terule_vector(diff, TERULE_OFFSET_MEMBER);
+}
+
+const apol_vector_t *poldiff_get_terule_vector_change(const poldiff_t * diff)
+{
+ return poldiff_get_terule_vector(diff, TERULE_OFFSET_CHANGE);
+}
+
+const apol_vector_t *poldiff_get_terule_vector_trans(const poldiff_t * diff)
+{
+ return poldiff_get_terule_vector(diff, TERULE_OFFSET_TRANS);
+}
+
+poldiff_form_e poldiff_terule_get_form(const void *terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_terule_t *)terule)->form;
+}
+
+uint32_t poldiff_terule_get_rule_type(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return terule->spec;
+}
+
+const char *poldiff_terule_get_source_type(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return terule->source;
+}
+
+const char *poldiff_terule_get_target_type(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return terule->target;
+}
+
+const char *poldiff_terule_get_object_class(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return terule->cls;
+}
+
+void poldiff_terule_get_cond(const poldiff_t * diff, const poldiff_terule_t * terule,
+ const qpol_cond_t ** cond, uint32_t * which_list, const apol_policy_t ** p)
+{
+ if (diff == NULL || terule == NULL || cond == NULL || p == NULL) {
+ errno = EINVAL;
+ return;
+ }
+ *cond = terule->cond;
+ if (*cond == NULL) {
+ *which_list = 1;
+ *p = NULL;
+ } else if (terule->form == POLDIFF_FORM_ADDED || terule->form == POLDIFF_FORM_ADD_TYPE) {
+ *which_list = terule->branch;
+ *p = diff->mod_pol;
+ } else {
+ *which_list = terule->branch;
+ *p = diff->orig_pol;
+ }
+}
+
+const char *poldiff_terule_get_original_default(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return terule->orig_default;
+}
+
+const char *poldiff_terule_get_modified_default(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return terule->mod_default;
+}
+
+apol_vector_t *poldiff_terule_get_orig_line_numbers(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return terule->orig_linenos;
+}
+
+apol_vector_t *poldiff_terule_get_mod_line_numbers(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return terule->mod_linenos;
+}
+
+/*************** protected functions ***************/
+
+/**
+ * Free all space used by a poldiff_terule_t, including the pointer
+ * itself. Does nothing if the pointer is already NULL.
+ *
+ * @param elem Pointer to a poldiff_terule_t.
+ */
+static void poldiff_terule_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_terule_t *t = elem;
+ apol_vector_destroy(&t->orig_linenos);
+ apol_vector_destroy(&t->mod_linenos);
+ free(t->orig_rules);
+ free(t->mod_rules);
+ free(elem);
+ }
+}
+
+poldiff_terule_summary_t *terule_create(void)
+{
+ poldiff_terule_summary_t *rs = calloc(1, sizeof(*rs));
+ if (rs == NULL) {
+ return NULL;
+ }
+ if ((rs->diffs = apol_vector_create(poldiff_terule_free)) == NULL) {
+ terule_destroy(&rs);
+ return NULL;
+ }
+ return rs;
+}
+
+void terule_destroy(poldiff_terule_summary_t ** rs)
+{
+ if (rs != NULL && *rs != NULL) {
+ apol_vector_destroy(&(*rs)->diffs);
+ free(*rs);
+ *rs = NULL;
+ }
+}
+
+/**
+ * Reset the state of all TE rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @param idx Index into the terule diffs array indicating which rule
+ * type to reset, one of TERULE_OFFSET_CHANGE, etc.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+static int terule_reset(poldiff_t * diff, terule_offset_e idx)
+{
+ int error = 0;
+ terule_destroy(&diff->terule_diffs[idx]);
+ diff->terule_diffs[idx] = terule_create();
+ if (diff->terule_diffs[idx] == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
+
+int terule_reset_change(poldiff_t * diff)
+{
+ return terule_reset(diff, TERULE_OFFSET_CHANGE);
+}
+
+int terule_reset_member(poldiff_t * diff)
+{
+ return terule_reset(diff, TERULE_OFFSET_MEMBER);
+}
+
+int terule_reset_trans(poldiff_t * diff)
+{
+ return terule_reset(diff, TERULE_OFFSET_TRANS);
+}
+
+static void terule_free_item(void *item)
+{
+ pseudo_terule_t *t = (pseudo_terule_t *) item;
+ if (item != NULL) {
+ free(t->rules);
+ free(t);
+ }
+}
+
+/**
+ * Apply an ordering scheme to two pseudo-te rules.
+ *
+ * <ul>
+ * <li>Sort by target pseudo-type value,
+ * <li>Then by source pseudo-type value,
+ * <li>Then by object class's BST's pointer value,
+ * <li>Then by rule specified (allow, neverallow, etc.),
+ * <li>Then choose unconditional rules over conditional rules,
+ * <li>Then by conditional expression's BST's boolean pointer value.
+ * </ul>
+ *
+ * If this function is being used for sorting (via terule_get_items())
+ * then sort by truth value, and then by branch (true branch, then
+ * false branch). Otherwise, when comparing rules (via terule_comp())
+ * then by truth value, inverting rule2's value if in the other
+ * branch.
+ */
+static int pseudo_terule_comp(const pseudo_terule_t * rule1, const pseudo_terule_t * rule2, int is_sorting)
+{
+ size_t i;
+ uint32_t bool_val;
+ if (rule1->target != rule2->target) {
+ return rule1->target - rule2->target;
+ }
+ if (rule1->source != rule2->source) {
+ return rule1->source - rule2->source;
+ }
+ if (rule1->cls != rule2->cls) {
+ return (int)(rule1->cls - rule2->cls);
+ }
+ if (rule1->spec != rule2->spec) {
+ return rule1->spec - rule2->spec;
+ }
+ if (rule1->bools[0] == NULL && rule2->bools[0] == NULL) {
+ /* both rules are unconditional */
+ return 0;
+ } else if (rule1->bools[0] == NULL && rule2->bools[0] != NULL) {
+ /* unconditional rules come before conditional */
+ return -1;
+ } else if (rule1->bools[0] != NULL && rule2->bools[0] == NULL) {
+ /* unconditional rules come before conditional */
+ return 1;
+ }
+ for (i = 0; i < (sizeof(rule1->bools) / sizeof(rule1->bools[0])); i++) {
+ if (rule1->bools[i] != rule2->bools[i]) {
+ return (int)(rule1->bools[i] - rule2->bools[i]);
+ }
+ }
+ if (is_sorting) {
+ if (rule1->branch != rule2->branch) {
+ return rule1->branch - rule2->branch;
+ }
+ return (int)rule1->bool_val - (int)rule2->bool_val;
+ } else {
+ if (rule1->branch == rule2->branch) {
+ bool_val = rule2->bool_val;
+ } else {
+ bool_val = ~rule2->bool_val;
+ }
+ if (rule1->bool_val < bool_val) {
+ return -1;
+ } else if (rule1->bool_val > bool_val) {
+ return 1;
+ }
+ return 0;
+ }
+}
+
+static int terule_bst_comp(const void *x, const void *y, void *data)
+{
+ const pseudo_terule_t *r1 = (const pseudo_terule_t *)x;
+ const pseudo_terule_t *r2 = (const pseudo_terule_t *)y;
+ poldiff_t *diff = data;
+ int retv;
+ retv = pseudo_terule_comp(r1, r2, 1);
+ if (!retv && r1->default_type != r2->default_type)
+ WARN(diff, "Multiple %s rules for %s %s %s with different default types", apol_rule_type_to_str(r1->spec),
+ type_map_get_name(diff, r1->source, POLDIFF_POLICY_ORIG), type_map_get_name(diff, r1->target,
+ POLDIFF_POLICY_ORIG), r1->cls);
+ return retv;
+}
+
+/**
+ * Given a conditional expression, convert its booleans to a sorted
+ * array of pseudo-boolean values, assign that array to the
+ * pseudo-terule key, and then derive the truth table.
+ *
+ * @param diff Policy difference structure.
+ * @param p Policy containing conditional.
+ * @param cond Conditional expression to convert.
+ * @param key Location to write converted expression.
+ */
+static int terule_build_cond(const poldiff_t * diff, const apol_policy_t * p, const qpol_cond_t * cond, pseudo_terule_t * key)
+{
+ qpol_iterator_t *iter = NULL;
+ qpol_cond_expr_node_t *node;
+ uint32_t expr_type, truthiness;
+ qpol_bool_t *bools[5] = { NULL, NULL, NULL, NULL, NULL }, *qbool;
+ size_t i, j;
+ size_t num_bools = 0;
+ const char *bool_name, *pseudo_bool, *t;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1, error = 0, compval;
+ if (qpol_cond_get_expr_node_iter(q, cond, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&node) < 0 || qpol_cond_expr_node_get_expr_type(q, node, &expr_type) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (expr_type != QPOL_COND_EXPR_BOOL) {
+ continue;
+ }
+ if (qpol_cond_expr_node_get_bool(q, node, &qbool) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (i = 0; i < num_bools; i++) {
+ if (bools[i] == qbool) {
+ break;
+ }
+ }
+ if (i >= num_bools) {
+ assert(i < 5);
+ bools[i] = qbool;
+ num_bools++;
+ }
+ }
+ for (i = 0; i < num_bools; i++) {
+ if (qpol_bool_get_name(q, bools[i], &bool_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_bst_get_element(diff->bool_bst, (void *)bool_name, NULL, (void **)&pseudo_bool) < 0) {
+ error = EBADRQC; /* should never get here */
+ ERR(diff, "%s", strerror(error));
+ assert(0);
+ goto cleanup;
+ }
+ key->bools[i] = pseudo_bool;
+ }
+
+ /* bubble sorth the pseudo bools (not bad because there are at
+ * most five elements */
+ for (i = num_bools; i > 1; i--) {
+ for (j = 1; j < i; j++) {
+ compval = strcmp(key->bools[j - 1], key->bools[j]);
+ if (compval > 0) {
+ t = key->bools[j];
+ key->bools[j] = key->bools[j - 1];
+ key->bools[j - 1] = t;
+ qbool = bools[j];
+ bools[j] = bools[j - 1];
+ bools[j - 1] = qbool;
+ }
+ }
+ }
+
+ /* now compute the truth table for the booleans */
+ key->bool_val = 0;
+ for (i = 0; i < 32; i++) {
+ for (j = 0; j < num_bools; j++) {
+ int state = ((i & (1 << j)) ? 1 : 0);
+ if (qpol_bool_set_state_no_eval(q, bools[j], state) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if (qpol_cond_eval(q, cond, &truthiness) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ key->bool_val = (key->bool_val << 1) | truthiness;
+ }
+
+ key->cond = cond;
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Given a rule, construct a new pseudo-terule and insert it into the
+ * BST if not already there.
+ *
+ * @param diff Policy difference structure.
+ * @param p Policy from which the rule came.
+ * @param rule TE rule to insert.
+ * @param source Source pseudo-type value.
+ * @param target Target pseudo-type value.
+ * @param b BST containing pseudo-terules.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int terule_add_to_bst(poldiff_t * diff, const apol_policy_t * p,
+ const qpol_terule_t * rule, uint32_t source, uint32_t target, apol_bst_t * b)
+{
+ pseudo_terule_t *key, *inserted_key;
+ const qpol_class_t *obj_class;
+ const qpol_type_t *default_type;
+ const char *class_name;
+ const qpol_cond_t *cond;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1, error = 0, compval;
+ int which = (p == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD);
+ if ((key = calloc(1, sizeof(*key))) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (qpol_terule_get_rule_type(q, rule, &(key->spec)) < 0 ||
+ qpol_terule_get_object_class(q, rule, &obj_class) < 0 ||
+ qpol_terule_get_default_type(q, rule, &default_type) < 0 || qpol_terule_get_cond(q, rule, &cond) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (qpol_class_get_name(q, obj_class, &class_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_bst_get_element(diff->class_bst, (void *)class_name, NULL, (void **)&key->cls) < 0) {
+ error = EBADRQC; /* should never get here */
+ ERR(diff, "%s", strerror(error));
+ assert(0);
+ goto cleanup;
+ }
+ if ((key->default_type = type_map_lookup(diff, default_type, which)) == 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ key->source = source;
+ key->target = target;
+ if (cond != NULL && (qpol_terule_get_which_list(q, rule, &(key->branch)) < 0 || terule_build_cond(diff, p, cond, key) < 0)) {
+ error = errno;
+ goto cleanup;
+ }
+
+ /* insert this pseudo into the tree if not already there */
+ if ((compval = apol_bst_insert_and_get(b, (void **)&key, diff)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ inserted_key = key;
+ key = NULL;
+
+ /* store the rule pointer, to be used for showing line numbers */
+ if (qpol_policy_has_capability(q, QPOL_CAP_LINE_NUMBERS)) {
+ const qpol_terule_t **t = realloc(inserted_key->rules,
+ (inserted_key->num_rules + 1) * sizeof(*t));
+ if (t == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ inserted_key->rules = t;
+ inserted_key->rules[inserted_key->num_rules++] = rule;
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval < 0) {
+ terule_free_item(key);
+ }
+ errno = error;
+ return retval;
+}
+
+/**
+ * Given a rule, expand its source and target types into individual
+ * pseudo-type values. Then add the expanded rule to the BST. This
+ * is needed for when the source and/or target is an attribute.
+ *
+ * @param diff Policy difference structure.
+ * @param p Policy from which the rule came.
+ * @param rule TE rule to insert.
+ * @param b BST containing pseudo-terules.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int terule_expand(poldiff_t * diff, const apol_policy_t * p, const qpol_terule_t * rule, apol_bst_t * b)
+{
+ const qpol_type_t *source, *orig_target, *target;
+ unsigned char source_attr, target_attr;
+ qpol_iterator_t *source_iter = NULL, *target_iter = NULL;
+ uint32_t source_val, target_val;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int which = (p == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD);
+ int retval = -1, error = 0;
+ if (qpol_terule_get_source_type(q, rule, &source) < 0 ||
+ qpol_terule_get_target_type(q, rule, &orig_target) < 0 ||
+ qpol_type_get_isattr(q, source, &source_attr) < 0 || qpol_type_get_isattr(q, orig_target, &target_attr)) {
+ error = errno;
+ goto cleanup;
+ }
+ if (source_attr && qpol_type_get_type_iter(q, source, &source_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ do {
+ if (source_attr) {
+ if (qpol_iterator_get_item(source_iter, (void **)&source) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ qpol_iterator_next(source_iter);
+ }
+ if (target_attr) {
+ if (qpol_type_get_type_iter(q, orig_target, &target_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ } else {
+ target = orig_target;
+ }
+ do {
+ if (target_attr) {
+ if (qpol_iterator_get_item(target_iter, (void **)&target) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ qpol_iterator_next(target_iter);
+ }
+ const char *n1, *n2;
+ qpol_type_get_name(q, source, &n1);
+ qpol_type_get_name(q, target, &n2);
+ if ((source_val = type_map_lookup(diff, source, which)) == 0 ||
+ (target_val = type_map_lookup(diff, target, which)) == 0 ||
+ terule_add_to_bst(diff, p, rule, source_val, target_val, b) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ } while (target_attr && !qpol_iterator_end(target_iter));
+ qpol_iterator_destroy(&target_iter);
+ } while (source_attr && !qpol_iterator_end(source_iter));
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&source_iter);
+ qpol_iterator_destroy(&target_iter);
+ errno = error;
+ return retval;
+}
+
+/**
+ * Get a vector of terules from the given policy, sorted. This
+ * function will remap source and target types to their pseudo-type
+ * value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ * @param which Kind of rule to get, one of QPOL_RULE_TYPE_TRANS, etc.
+ *
+ * @return A newly allocated vector of all te rules (of type
+ * pseudo_terule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+static apol_vector_t *terule_get_items(poldiff_t * diff, const apol_policy_t * policy, unsigned int which)
+{
+ apol_vector_t *bools = NULL, *bool_states = NULL;
+ size_t i, num_rules, j;
+ apol_bst_t *b = NULL;
+ apol_vector_t *v = NULL;
+ qpol_iterator_t *iter = NULL;
+ qpol_terule_t *rule;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int retval = -1, error = 0;
+ if (poldiff_build_bsts(diff) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+
+ /* store original boolean values */
+ if (apol_bool_get_by_query(policy, NULL, &bools) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((bool_states = apol_vector_create_with_capacity(apol_vector_get_size(bools), NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(bools); i++) {
+ qpol_bool_t *qbool = apol_vector_get_element(bools, i);
+ int state;
+ if (qpol_bool_get_state(q, qbool, &state) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_append(bool_states, (void *)((size_t) state)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if ((b = apol_bst_create(terule_bst_comp, terule_free_item)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (qpol_policy_get_terule_iter(q, which, &iter) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ qpol_iterator_get_size(iter, &num_rules);
+ for (j = 0; !qpol_iterator_end(iter); qpol_iterator_next(iter), j++) {
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0 || terule_expand(diff, policy, rule, b) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (!(j % 1024)) {
+ int percent = 50 * j / num_rules + (policy == diff->mod_pol ? 50 : 0);
+ INFO(diff, "Computing TE rule difference: %02d%% complete", percent);
+ }
+ }
+ if ((v = apol_bst_get_vector(b, 1)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ /* restore boolean states */
+ for (i = 0; bools != NULL && i < apol_vector_get_size(bools); i++) {
+ qpol_bool_t *qbool = apol_vector_get_element(bools, i);
+ int state = (int)((size_t) apol_vector_get_element(bool_states, i));
+ qpol_bool_set_state_no_eval(q, qbool, state);
+ }
+ apol_vector_destroy(&bools);
+ apol_vector_destroy(&bool_states);
+ qpol_policy_reevaluate_conds(q);
+ apol_bst_destroy(&b);
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+apol_vector_t *terule_get_items_change(poldiff_t * diff, const apol_policy_t * policy)
+{
+ return terule_get_items(diff, policy, QPOL_RULE_TYPE_CHANGE);
+}
+
+apol_vector_t *terule_get_items_member(poldiff_t * diff, const apol_policy_t * policy)
+{
+ return terule_get_items(diff, policy, QPOL_RULE_TYPE_MEMBER);
+}
+
+apol_vector_t *terule_get_items_trans(poldiff_t * diff, const apol_policy_t * policy)
+{
+ return terule_get_items(diff, policy, QPOL_RULE_TYPE_TRANS);
+}
+
+int terule_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused)))
+{
+ const pseudo_terule_t *r1 = (const pseudo_terule_t *)x;
+ const pseudo_terule_t *r2 = (const pseudo_terule_t *)y;
+ return pseudo_terule_comp(r1, r2, 0);
+}
+
+/**
+ * Allocate and return a new terule difference object. If the
+ * pseudo-terule's source and/or target expands to multiple read
+ * types, then just choose the first one for display.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param rule Pseudo terule that changed.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling poldiff_terule_free() upon
+ * the returned value.
+ */
+static poldiff_terule_t *make_tediff(const poldiff_t * diff, poldiff_form_e form, const pseudo_terule_t * rule)
+{
+ poldiff_terule_t *pt;
+ const char *n1, *n2;
+ int error;
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ n1 = type_map_get_name(diff, rule->source, POLDIFF_POLICY_MOD);
+ n2 = type_map_get_name(diff, rule->target, POLDIFF_POLICY_MOD);
+ } else {
+ n1 = type_map_get_name(diff, rule->source, POLDIFF_POLICY_ORIG);
+ n2 = type_map_get_name(diff, rule->target, POLDIFF_POLICY_ORIG);
+ }
+ assert(n1 != NULL && n2 != NULL);
+ if ((pt = calloc(1, sizeof(*pt))) == NULL) {
+ error = errno;
+ poldiff_terule_free(pt);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pt->spec = rule->spec;
+ pt->source = n1;
+ pt->target = n2;
+ pt->cls = rule->cls;
+ pt->form = form;
+ pt->cond = rule->cond;
+ pt->branch = rule->branch;
+ return pt;
+}
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-te rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ * @param idx Index into the terule differences specifying int which
+ * to place the constructed pseudo-te rule.
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+static int terule_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item, terule_offset_e idx)
+{
+ pseudo_terule_t *rule = (pseudo_terule_t *) item;
+ poldiff_terule_t *pt = NULL;
+ const apol_vector_t *v1, *v2;
+ apol_policy_t *p;
+ const char *orig_default = NULL, *mod_default = NULL;
+ int retval = -1, error = errno;
+
+ /* check if form should really become ADD_TYPE / REMOVE_TYPE,
+ * by seeing if the /other/ policy's reverse lookup is
+ * empty */
+ if (form == POLDIFF_FORM_ADDED) {
+ if ((v1 = type_map_lookup_reverse(diff, rule->source, POLDIFF_POLICY_ORIG)) == NULL ||
+ (v2 = type_map_lookup_reverse(diff, rule->target, POLDIFF_POLICY_ORIG)) == NULL ||
+ (mod_default = type_map_get_name(diff, rule->default_type, POLDIFF_POLICY_MOD)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) {
+ form = POLDIFF_FORM_ADD_TYPE;
+ }
+ p = diff->mod_pol;
+ } else {
+ if ((v1 = type_map_lookup_reverse(diff, rule->source, POLDIFF_POLICY_MOD)) == NULL ||
+ (v2 = type_map_lookup_reverse(diff, rule->target, POLDIFF_POLICY_MOD)) == NULL ||
+ (orig_default = type_map_get_name(diff, rule->default_type, POLDIFF_POLICY_ORIG)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) {
+ form = POLDIFF_FORM_REMOVE_TYPE;
+ }
+ p = diff->orig_pol;
+ }
+
+ pt = make_tediff(diff, form, rule);
+ if (pt == NULL) {
+ return -1;
+ }
+ pt->orig_default = orig_default;
+ pt->mod_default = mod_default;
+
+ /* calculate line numbers */
+ if (qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_LINE_NUMBERS)) {
+ apol_vector_t *vl = NULL;
+ if ((vl = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ pt->mod_linenos = vl;
+ } else {
+ pt->orig_linenos = vl;
+ }
+
+ /* copy rule pointers for delayed line number claculation */
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ pt->num_mod_rules = rule->num_rules;
+ pt->mod_rules = calloc(rule->num_rules, sizeof(qpol_terule_t *));
+ if (!pt->mod_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pt->mod_rules, rule->rules, rule->num_rules * sizeof(qpol_terule_t *));
+ } else {
+ pt->num_orig_rules = rule->num_rules;
+ pt->orig_rules = calloc(rule->num_rules, sizeof(qpol_terule_t *));
+ if (!pt->orig_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pt->orig_rules, rule->rules, rule->num_rules * sizeof(qpol_terule_t *));
+ }
+ }
+
+ if (apol_vector_append(diff->terule_diffs[idx]->diffs, pt) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ diff->terule_diffs[idx]->num_added++;
+ break;
+ case POLDIFF_FORM_ADD_TYPE:
+ diff->terule_diffs[idx]->num_added_type++;
+ break;
+ case POLDIFF_FORM_REMOVED:
+ diff->terule_diffs[idx]->num_removed++;
+ break;
+ case POLDIFF_FORM_REMOVE_TYPE:
+ diff->terule_diffs[idx]->num_removed_type++;
+ break;
+ default:
+ error = EBADRQC; /* should never get here */
+ ERR(diff, "%s", strerror(error));
+ assert(0);
+ goto cleanup;
+ }
+ diff->terule_diffs[idx]->diffs_sorted = 0;
+ retval = 0;
+ cleanup:
+ if (retval < 0) {
+ poldiff_terule_free(pt);
+ }
+ errno = error;
+ return retval;
+}
+
+int terule_new_diff_change(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ return terule_new_diff(diff, form, item, TERULE_OFFSET_CHANGE);
+}
+
+int terule_new_diff_member(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ return terule_new_diff(diff, form, item, TERULE_OFFSET_MEMBER);
+}
+
+int terule_new_diff_trans(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ return terule_new_diff(diff, form, item, TERULE_OFFSET_TRANS);
+}
+
+/**
+ * Compute the semantic difference of two pseudo-te rules for which
+ * the compare callback returns 0. If a difference is found then
+ * allocate, initialize, and insert a new semantic difference entry
+ * for that pseudo-te rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-te rules and to which to add an entry if needed.
+ * @param x The pseudo-te rule from the original policy.
+ * @param y The pseudo-te rule from the modified policy.
+ * @param idx Index into the terule differences specifying into which
+ * to place the constructed pseudo-ate rule.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+static int terule_deep_diff(poldiff_t * diff, const void *x, const void *y, terule_offset_e idx)
+{
+ pseudo_terule_t *r1 = (pseudo_terule_t *) x;
+ pseudo_terule_t *r2 = (pseudo_terule_t *) y;
+ poldiff_terule_t *pt = NULL;
+ int retval = -1, error = 0;
+
+ if (r1->default_type != r2->default_type) {
+ if ((pt = make_tediff(diff, POLDIFF_FORM_MODIFIED, r1)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ pt->orig_default = type_map_get_name(diff, r1->default_type, POLDIFF_POLICY_ORIG);
+ pt->mod_default = type_map_get_name(diff, r2->default_type, POLDIFF_POLICY_MOD);
+
+ /* calculate line numbers */
+ if (qpol_policy_has_capability(apol_policy_get_qpol(diff->orig_pol), QPOL_CAP_LINE_NUMBERS)) {
+ if ((pt->orig_linenos = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ /* copy rule pointers for delayed line number claculation */
+ pt->num_orig_rules = r1->num_rules;
+ pt->orig_rules = calloc(r1->num_rules, sizeof(qpol_terule_t *));
+ if (!pt->orig_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pt->orig_rules, r1->rules, r1->num_rules * sizeof(qpol_terule_t *));
+ }
+ if (qpol_policy_has_capability(apol_policy_get_qpol(diff->mod_pol), QPOL_CAP_LINE_NUMBERS)) {
+ if ((pt->mod_linenos = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ /* copy rule pointers for delayed line number claculation */
+ pt->num_mod_rules = r2->num_rules;
+ pt->mod_rules = calloc(r2->num_rules, sizeof(qpol_terule_t *));
+ if (!pt->mod_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pt->mod_rules, r2->rules, r2->num_rules * sizeof(qpol_terule_t *));
+ }
+
+ if (apol_vector_append(diff->terule_diffs[idx]->diffs, pt) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->terule_diffs[idx]->num_modified++;
+ diff->terule_diffs[idx]->diffs_sorted = 0;
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ poldiff_terule_free(pt);
+ }
+ errno = error;
+ return retval;
+}
+
+int terule_deep_diff_change(poldiff_t * diff, const void *x, const void *y)
+{
+ return terule_deep_diff(diff, x, y, TERULE_OFFSET_CHANGE);
+}
+
+int terule_deep_diff_member(poldiff_t * diff, const void *x, const void *y)
+{
+ return terule_deep_diff(diff, x, y, TERULE_OFFSET_MEMBER);
+}
+
+int terule_deep_diff_trans(poldiff_t * diff, const void *x, const void *y)
+{
+ return terule_deep_diff(diff, x, y, TERULE_OFFSET_TRANS);
+}
+
+int terule_enable_line_numbers(poldiff_t * diff, terule_offset_e idx)
+{
+ const apol_vector_t *te = NULL;
+ poldiff_terule_t *terule = NULL;
+ size_t i, j;
+ qpol_iterator_t *iter = NULL;
+ qpol_syn_terule_t *ste = NULL;
+ int error = 0;
+ unsigned long lineno = 0;
+
+ te = poldiff_get_terule_vector(diff, idx);
+
+ for (i = 0; i < apol_vector_get_size(te); i++) {
+ terule = apol_vector_get_element(te, i);
+ if (apol_vector_get_size(terule->mod_linenos) || apol_vector_get_size(terule->orig_linenos))
+ continue;
+ for (j = 0; j < terule->num_orig_rules; j++) {
+ if (qpol_terule_get_syn_terule_iter(diff->orig_qpol, terule->orig_rules[j], &iter)) {
+ error = errno;
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&ste) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_syn_terule_get_lineno(diff->orig_qpol, ste, &lineno) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (apol_vector_append(terule->orig_linenos, (void *)lineno) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_sort_uniquify(terule->orig_linenos, NULL, NULL);
+ for (j = 0; j < terule->num_mod_rules; j++) {
+ if (qpol_terule_get_syn_terule_iter(diff->mod_qpol, terule->mod_rules[j], &iter)) {
+ error = errno;
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&ste) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_syn_terule_get_lineno(diff->mod_qpol, ste, &lineno) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (apol_vector_append(terule->mod_linenos, (void *)lineno) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_sort_uniquify(terule->mod_linenos, NULL, NULL);
+ }
+
+ return 0;
+ err:
+ qpol_iterator_destroy(&iter);
+ return -1;
+}
diff --git a/libpoldiff/src/terule_internal.h b/libpoldiff/src/terule_internal.h
new file mode 100644
index 0000000..79b4441
--- /dev/null
+++ b/libpoldiff/src/terule_internal.h
@@ -0,0 +1,244 @@
+/**
+ * @file
+ * Protected interface for rule differences, both AV and Type 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 POLDIFF_TERULE_INTERNAL_H
+#define POLDIFF_TERULE_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_terule_summary poldiff_terule_summary_t;
+
+/**
+ * Allocate and return a new poldiff_terule_summary_t object, used by
+ * TE rule searches.
+ *
+ * @return A new rule summary. The caller must call terule_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_terule_summary_t *terule_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_terule_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param rs Reference to an rule summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void terule_destroy(poldiff_terule_summary_t ** rs);
+
+/**
+ * Reset the state of TE rule type_change differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int terule_reset_change(poldiff_t * diff);
+
+/**
+ * Reset the state of TE rule type_member differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int terule_reset_member(poldiff_t * diff);
+
+/**
+ * Reset the state of TE rule type_transition differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int terule_reset_trans(poldiff_t * diff);
+
+/**
+ * Get a vector of type_change rules from the given policy, sorted.
+ * This function will remap source and target types to their
+ * pseudo-type value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of type_change rules (of type
+ * pseudo_terule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *terule_get_items_change(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Get a vector of type_member rules from the given policy, sorted.
+ * This function will remap source and target types to their
+ * pseudo-type value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ * @param flags Kind of rule to get, one of QPOL_RULE_TYPE_TRANS, etc.
+ *
+ * @return A newly allocated vector of type_member rules (of type
+ * pseudo_terule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *terule_get_items_member(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Get a vector of type_transition rules from the given policy,
+ * sorted. This function will remap source and target types to their
+ * pseudo-type value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of type_transition rules (of type
+ * pseudo_terule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *terule_get_items_trans(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two pseudo_terule_t objects, determining if they have the
+ * same key (specified + source + target + class + conditional
+ * expression).
+ *
+ * @param x The pseudo-te rule from the original policy.
+ * @param y The pseudo-te rule from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if te rule x is respectively less than,
+ * equal to, or greater than te rule y.
+ */
+ int terule_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-te rule that was originally from a type_change rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int terule_new_diff_change(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-te rule that was originally from a type_member rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int terule_new_diff_member(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-te rule that was originally from a type_transition rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int terule_new_diff_trans(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two pseudo-te rules (that were
+ * type_change rules) for which the compare callback returns 0. If a
+ * difference is found then allocate, initialize, and insert a new
+ * semantic difference entry for that pseudo-te rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-te rules and to which to add an entry if needed.
+ * @param x The pseudo-te rule from the original policy.
+ * @param y The pseudo-te rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int terule_deep_diff_change(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Compute the semantic difference of two pseudo-te rules (that were
+ * type_member rules) for which the compare callback returns 0. If a
+ * difference is found then allocate, initialize, and insert a new
+ * semantic difference entry for that pseudo-te rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-te rules and to which to add an entry if needed.
+ * @param x The pseudo-te rule from the original policy.
+ * @param y The pseudo-te rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int terule_deep_diff_member(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Compute the semantic difference of two pseudo-te rules (that were
+ * type_transition rules) for which the compare callback returns 0.
+ * If a difference is found then allocate, initialize, and insert a
+ * new semantic difference entry for that pseudo-te rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-te rules and to which to add an entry if needed.
+ * @param x The pseudo-te rule from the original policy.
+ * @param y The pseudo-te rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int terule_deep_diff_trans(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Iterate through a TE rule differences, filling in its line numbers.
+ *
+ * @param diff Diff structure containing avrule differences.
+ * @param idx Index into the terule differences specifying which line
+ * number table to enable.
+ *
+ * @return 0 on success, < 0 on errno.
+ */
+ int terule_enable_line_numbers(poldiff_t * diff, unsigned int idx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_TERULE_INTERNAL_H */
diff --git a/libpoldiff/src/type_diff.c b/libpoldiff/src/type_diff.c
new file mode 100644
index 0000000..32c7a17
--- /dev/null
+++ b/libpoldiff/src/type_diff.c
@@ -0,0 +1,662 @@
+/**
+ * @file
+ * Implementation for computing a semantic differences in types.
+ *
+ * @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 "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+/******************** types ********************/
+
+struct poldiff_type_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ int are_diffs_sorted;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_type
+{
+ char *name;
+ poldiff_form_e form;
+ apol_vector_t *added_attribs;
+ apol_vector_t *removed_attribs;
+};
+
+void poldiff_type_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->type_diffs->num_added;
+ stats[1] = diff->type_diffs->num_removed;
+ stats[2] = diff->type_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+char *poldiff_type_to_string(const poldiff_t * diff, const void *type)
+{
+ poldiff_type_t *t = (poldiff_type_t *) type;
+ size_t num_added, num_removed, len = 0, i;
+ char *s = NULL, *attrib;
+
+ if (diff == NULL || type == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ num_added = apol_vector_get_size(t->added_attribs);
+ num_removed = apol_vector_get_size(t->removed_attribs);
+ switch (t->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", t->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", t->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "* %s (", t->name) < 0) {
+ break;
+ }
+ if (num_added > 0) {
+ if (apol_str_appendf(&s, &len, "%zd Added Attribute%s", num_added, (num_added == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (num_removed > 0) {
+ if (apol_str_appendf
+ (&s, &len, "%s%zd Removed Attribute%s", (num_added > 0 ? ", " : ""), num_removed,
+ (num_removed == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (apol_str_append(&s, &len, ")\n") < 0) {
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(t->added_attribs); i++) {
+ attrib = (char *)apol_vector_get_element(t->added_attribs, i);
+ if (apol_str_appendf(&s, &len, "\t+ %s\n", attrib) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(t->removed_attribs); i++) {
+ attrib = (char *)apol_vector_get_element(t->removed_attribs, i);
+ if (apol_str_appendf(&s, &len, "\t- %s\n", attrib) < 0) {
+ goto err;
+ }
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ err:
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+static int poldiff_type_comp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const poldiff_type_t *t1 = a;
+ const poldiff_type_t *t2 = b;
+ return strcmp(t1->name, t2->name);
+}
+
+const apol_vector_t *poldiff_get_type_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ /* the elements of the results vector are not sorted by name,
+ * but by pseudo-type value. thus sort them by name as
+ * necessary */
+ if (!diff->type_diffs->are_diffs_sorted) {
+ apol_vector_sort(diff->type_diffs->diffs, poldiff_type_comp, NULL);
+ diff->type_diffs->are_diffs_sorted = 1;
+ }
+ return diff->type_diffs->diffs;
+}
+
+const char *poldiff_type_get_name(const poldiff_type_t * type)
+{
+ if (type == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return type->name;
+}
+
+poldiff_form_e poldiff_type_get_form(const void *type)
+{
+ if (type == NULL) {
+ errno = EINVAL;
+ return POLDIFF_FORM_NONE;
+ }
+ return ((const poldiff_type_t *)type)->form;
+}
+
+const apol_vector_t *poldiff_type_get_added_attribs(const poldiff_type_t * type)
+{
+ if (type == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return type->added_attribs;
+}
+
+const apol_vector_t *poldiff_type_get_removed_attribs(const poldiff_type_t * type)
+{
+ if (type == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return type->removed_attribs;
+}
+
+/*************** protected functions for types ***************/
+
+/**
+ * Destroy a specified type
+ * @param type The type to destroy (a poldiff_type_t object)
+ */
+static void type_destroy(void *type)
+{
+ poldiff_type_t *t;
+ if (type == NULL)
+ return;
+ t = (poldiff_type_t *) type;
+ free(t->name);
+ apol_vector_destroy(&(t->added_attribs));
+ apol_vector_destroy(&(t->removed_attribs));
+ free(t);
+}
+
+poldiff_type_summary_t *type_summary_create(void)
+{
+ poldiff_type_summary_t *type = calloc(1, sizeof(*type));
+ if (type == NULL) {
+ return NULL;
+ }
+ if ((type->diffs = apol_vector_create(type_destroy)) == NULL) {
+ type_summary_destroy(&type);
+ return NULL;
+ }
+ return type;
+}
+
+void type_summary_destroy(poldiff_type_summary_t ** type)
+{
+ if (type != NULL && *type != NULL) {
+ apol_vector_destroy(&(*type)->diffs);
+ free(*type);
+ *type = NULL;
+ }
+}
+
+apol_vector_t *type_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ int error = 0;
+ qpol_type_t *t;
+ unsigned char isattr, isalias;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ uint32_t val;
+
+ if (diff == NULL || policy == NULL) {
+ error = errno = EINVAL;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ if (qpol_policy_get_type_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create(NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)&t);
+ qpol_type_get_isalias(q, t, &isalias);
+ qpol_type_get_isattr(q, t, &isattr);
+ if (isattr || isalias)
+ continue;
+ val = type_map_lookup(diff, t, policy == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD);
+ apol_vector_append(v, (void *)((size_t) val));
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort_uniquify(v, NULL, NULL);
+ return v;
+}
+
+int type_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ type_summary_destroy(&diff->type_diffs);
+ diff->type_diffs = type_summary_create();
+ if (diff->type_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Compare two type map values
+ * @param x The first type to compare, a (uint32_t) value
+ * @param y The second type to compare, a (uint32_t) value
+ * @param diff The policy difference structure
+ *
+ * @return < 0, 0, or > 0, if x is respectively less than
+ * equal to, or greater than y.
+ */
+int type_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused)))
+{
+ uint32_t p1val = (uint32_t) ((size_t) x);
+ uint32_t p2val = (uint32_t) ((size_t) y);
+
+ /* p1val == p2val means the types are semantically equivalent */
+ return p1val - p2val;
+}
+
+/**
+ * Allocate and return a new type difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the type that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling type_destroy() upon the
+ * returned value.
+ */
+static poldiff_type_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_type_t *pt;
+ int error;
+
+ if ((pt = calloc(1, sizeof(poldiff_type_t))) == NULL ||
+ (pt->name = strdup(name)) == NULL ||
+ (pt->added_attribs = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pt->removed_attribs = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = errno;
+ type_destroy(pt);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pt->form = form;
+ return pt;
+}
+
+/**
+ * Allocate and return a string representing the type that is
+ * different. If the type was not remapped then it is simply that
+ * type's name within the policy. Otherwise the string will be of the
+ * form "orig_name1, orig_name2, ... -> mod_name1, mod_name2, ..."
+ *
+ * @param diff Diff structure containing type remaps.
+ * @param tval Pseudo-type value whose name to get.
+ *
+ * @return Allocated string name for the type, or NULL upon error.
+ * The caller must call free() upon the returned value.
+ */
+static char *type_get_name(const poldiff_t * diff, uint32_t tval)
+{
+ const apol_vector_t *v1, *v2;
+ size_t sv1, sv2;
+ size_t i, len = 0;
+ const qpol_type_t *qtype;
+ const char *name = NULL;
+ char *ret = NULL;
+ int error = 0;
+
+ /* names mapped from the first policy */
+ v1 = type_map_lookup_reverse(diff, tval, POLDIFF_POLICY_ORIG);
+ sv1 = apol_vector_get_size(v1);
+ /* names mapped from the second policy */
+ v2 = type_map_lookup_reverse(diff, tval, POLDIFF_POLICY_MOD);
+ sv2 = apol_vector_get_size(v2);
+
+ if (sv1 == 1 && sv2 == 0) {
+ /* return the name in v1 */
+ qtype = apol_vector_get_element(v1, 0);
+ if (qpol_type_get_name(diff->orig_qpol, qtype, &name) < 0 || (ret = strdup(name)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ } else if (sv1 == 0 && sv2 == 1) {
+ /* return the name in v2 */
+ qtype = apol_vector_get_element(v2, 0);
+ if (qpol_type_get_name(diff->mod_qpol, qtype, &name) < 0 || (ret = strdup(name)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ } else {
+ /* if the single name in v1 and v2 is the same return that name */
+ if (sv1 == sv2 && sv2 == 1) {
+ const char *name2;
+ qpol_type_t *qtype2;
+ qtype = apol_vector_get_element(v1, 0);
+ qtype2 = apol_vector_get_element(v2, 0);
+ if (qpol_type_get_name(diff->orig_qpol, qtype, &name) < 0 ||
+ qpol_type_get_name(diff->mod_qpol, qtype2, &name2) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (strcmp(name, name2) == 0) {
+ if ((ret = strdup(name)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ }
+ goto cleanup;
+ }
+ }
+ /* build and return the composite name */
+ for (i = 0; i < sv1; i++) {
+ qtype = apol_vector_get_element(v1, i);
+ if (qpol_type_get_name(diff->orig_qpol, qtype, &name) < 0 ||
+ apol_str_appendf(&ret, &len, "%s%s", (i > 0 ? ", " : ""), name) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ apol_str_append(&ret, &len, " -> ");
+ for (i = 0; i < sv2; i++) {
+ qtype = apol_vector_get_element(v2, i);
+ if (qpol_type_get_name(diff->mod_qpol, qtype, &name) < 0 ||
+ apol_str_appendf(&ret, &len, "%s%s", (i > 0 ? ", " : ""), name) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ }
+ cleanup:
+ if (error != 0) {
+ free(ret);
+ errno = error;
+ return NULL;
+ }
+ return ret;
+}
+
+int type_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ uint32_t tval = (uint32_t) ((size_t) item);
+ char *name = NULL;
+ poldiff_type_t *pt;
+ int error;
+
+ if ((name = type_get_name(diff, tval)) == NULL || (pt = make_diff(diff, form, name)) == NULL) {
+ error = errno;
+ free(name);
+ errno = error;
+ return -1;
+ }
+ free(name);
+ if (apol_vector_append(diff->type_diffs->diffs, pt) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ type_destroy(pt);
+ errno = error;
+ return -1;
+ }
+ diff->type_diffs->are_diffs_sorted = 0;
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->type_diffs->num_added++;
+ } else {
+ diff->type_diffs->num_removed++;
+ }
+ return 0;
+}
+
+/**
+ * Given an type, return a vector of its attributes (in the form of
+ * strings).
+ *
+ * @param diff Policy diff error handler.
+ * @param p Policy from which the type came.
+ * @param type Type whose attributes to get.
+ *
+ * @return Vector of attribute strings for the type. The caller is
+ * responsible for calling apol_vector_destroy(). On error, return
+ * NULL.
+ */
+static apol_vector_t *type_get_attrib_names(const poldiff_t * diff, const apol_policy_t * p, uint32_t type)
+{
+ qpol_iterator_t *attrib_iter = NULL;
+ const char *attrib;
+ char *new_attrib;
+ const apol_vector_t *v = NULL;
+ apol_vector_t *ret = NULL;
+ qpol_type_t *qt = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1;
+ size_t i;
+
+ if ((ret = apol_vector_create(free)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+
+ /* get the qpol_type_t objects for the specified type value
+ * and policy */
+ v = type_map_lookup_reverse(diff, type, (diff->orig_pol == p ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD));
+ if (apol_vector_get_size(v) == 0) {
+ assert(false);
+ return NULL;
+ }
+ /* append the attributes for each qpol_type_t to the vector we return */
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ qt = apol_vector_get_element(v, i);
+ if (qt == NULL) {
+ assert(false);
+ return NULL;
+ }
+ qpol_type_get_attr_iter(q, qt, &attrib_iter);
+ for (; !qpol_iterator_end(attrib_iter); qpol_iterator_next(attrib_iter)) {
+
+ if (qpol_iterator_get_item(attrib_iter, (void **)&qt) < 0) {
+ goto cleanup;
+ }
+ qpol_type_get_name(q, qt, &attrib);
+ if ((new_attrib = strdup(attrib)) == NULL || apol_vector_append(ret, new_attrib) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ }
+ apol_vector_sort_uniquify(ret, &apol_str_strcmp, NULL);
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&attrib_iter);
+ if (retval < 0) {
+ apol_vector_destroy(&ret);
+ return NULL;
+ }
+ return ret;
+}
+
+int type_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ uint32_t tval1 = (uint32_t) ((size_t) x);
+ uint32_t tval2 = (uint32_t) ((size_t) y);
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ char *attrib1 = NULL, *attrib2 = NULL, *name = NULL;
+ poldiff_type_t *t = NULL;
+ size_t i, j;
+ int retval = -1, error = 0, compval;
+
+ assert(tval1 == tval2);
+ /* can't do a deep diff of type if either policy does not retain attribute
+ * names because the fake attribute names are bogus */
+ if (!(qpol_policy_has_capability(apol_policy_get_qpol(diff->orig_pol), QPOL_CAP_ATTRIB_NAMES))
+ || !(qpol_policy_has_capability(apol_policy_get_qpol(diff->mod_pol), QPOL_CAP_ATTRIB_NAMES))) {
+ return 0;
+ }
+ v1 = type_get_attrib_names(diff, diff->orig_pol, tval1);
+ v2 = type_get_attrib_names(diff, diff->mod_pol, tval2);
+ apol_vector_sort(v1, apol_str_strcmp, NULL);
+ apol_vector_sort(v2, apol_str_strcmp, NULL);
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2))
+ break;
+ attrib1 = (char *)apol_vector_get_element(v1, i);
+ attrib2 = (char *)apol_vector_get_element(v2, j);
+ compval = strcmp(attrib1, attrib2);
+ if (compval != 0 && t == NULL) {
+ name = type_get_name(diff, tval1);
+ if ((t = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ free(name);
+ name = NULL;
+ }
+ if (compval < 0) {
+ if ((attrib1 = strdup(attrib1)) == NULL || apol_vector_append(t->removed_attribs, attrib1) < 0) {
+ error = errno;
+ free(attrib1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (compval > 0) {
+ if ((attrib2 = strdup(attrib2)) == NULL || apol_vector_append(t->added_attribs, attrib2) < 0) {
+ error = errno;
+ free(attrib2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ attrib1 = (char *)apol_vector_get_element(v1, i);
+ if (t == NULL) {
+ name = type_get_name(diff, tval1);
+ if ((t = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ free(name);
+ name = NULL;
+ }
+ if ((attrib1 = strdup(attrib1)) == NULL || apol_vector_append(t->removed_attribs, attrib1) < 0) {
+ error = errno;
+ free(attrib1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ attrib2 = (char *)apol_vector_get_element(v2, j);
+ if (t == NULL) {
+ name = type_get_name(diff, tval1);
+ if ((t = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ free(name);
+ name = NULL;
+ }
+ if ((attrib2 = strdup(attrib2)) == NULL || apol_vector_append(t->added_attribs, attrib2) < 0) {
+ error = errno;
+ free(attrib2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (t != NULL) {
+ if (apol_vector_append(diff->type_diffs->diffs, t) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->type_diffs->are_diffs_sorted = 0;
+ diff->type_diffs->num_modified++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ free(name);
+ if (retval != 0) {
+ type_destroy(t);
+ }
+ errno = error;
+ return retval;
+ return 0;
+}
diff --git a/libpoldiff/src/type_internal.h b/libpoldiff/src/type_internal.h
new file mode 100644
index 0000000..8c9cb44
--- /dev/null
+++ b/libpoldiff/src/type_internal.h
@@ -0,0 +1,125 @@
+/**
+ * @file
+ * Protected Interface for type differences.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @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 POLDIFF_TYPE_INTERNAL_H
+#define POLDIFF_TYPE_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/******************** types ********************/
+
+ typedef struct poldiff_type_summary poldiff_type_summary_t;
+
+/**
+ * Allocate and return a new poldiff_type_summary_t object.
+ *
+ * @return A new type summary. The caller must call type_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_type_summary_t *type_summary_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_type_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param type Reference to a type summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void type_summary_destroy(poldiff_type_summary_t ** type);
+
+/**
+ * Reset the state of all type differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int type_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all type (type qpol_type_t) from the
+ * given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all typees. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *type_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_type_t objects, determining if they have the same
+ * name or not.
+ *
+ * @param x The type from the original policy.
+ * @param y The type from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if type x is respectively less than, equal
+ * to, or greater than type y.
+ */
+ int type_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a type.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int type_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two types for which the
+ * compare callback returns 0. If a difference is found then
+ * allocate, initialize, and insert a new semantic difference entry
+ * for that type.
+ *
+ * @param diff The policy difference structure associated with both
+ * types and to which to add an entry if needed.
+ * @param x The type from the original policy.
+ * @param y The type from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int type_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_TYPE_INTERNAL_H */
diff --git a/libpoldiff/src/type_map.c b/libpoldiff/src/type_map.c
new file mode 100644
index 0000000..ff97c43
--- /dev/null
+++ b/libpoldiff/src/type_map.c
@@ -0,0 +1,985 @@
+/**
+ * @file
+ * Implementation of type equivalence mapping for semantic
+ * difference calculations.
+ * The mapping of types is handled by creating a list of pseudo type
+ * values to represent the set of all semantically unique types in
+ * both the original and modified policies. This mapping takes into
+ * account both inferred and user specified mappings of types and may
+ * contain holes where a type does not exist in one of the 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 "poldiff_internal.h"
+
+#include <apol/policy-query.h>
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * A poldiff's type map consists of maps between policies' types to a
+ * unified pseudo-type value.
+ */
+struct type_map
+{
+ /** array of size num_orig_types mapping types by (value - 1)
+ to pseudo value */
+ uint32_t *orig_to_pseudo;
+ /** array of size num_mod_types mapping types by (value - 1)
+ to pseudo value */
+ uint32_t *mod_to_pseudo;
+ /** vector of vector of qpol_type_t that reverse map pseudo
+ value to orig_pol value(s) */
+ apol_vector_t *pseudo_to_orig;
+ /** vector of vector of qpol_type_t that reverse map pseudo
+ value to mod_pol value(s) */
+ apol_vector_t *pseudo_to_mod;
+ size_t num_orig_types;
+ size_t num_mod_types;
+ /** vector of poldiff_type_remap_entry_t */
+ apol_vector_t *remap;
+};
+
+/**
+ * Each map entry consists of 2 vectors, each vector being a list of
+ * qpol_type_t.
+ */
+struct poldiff_type_remap_entry
+{
+ /** vector of names of qpol_type_t in original qpolicy */
+ apol_vector_t *orig_types;
+ /** vector of names of qpol_type_t in the modified qpolicy */
+ apol_vector_t *mod_types;
+ int inferred;
+ int enabled;
+};
+
+/**
+ * Free the space associated with a singly type remap entry.
+ *
+ * @param elem Pointer to a type remap entry to free. If NULL then do
+ * nothing.
+ */
+static void type_remap_entry_free(void *elem)
+{
+ poldiff_type_remap_entry_t *entry = (poldiff_type_remap_entry_t *) elem;
+ if (entry != NULL) {
+ apol_vector_destroy(&entry->orig_types);
+ apol_vector_destroy(&entry->mod_types);
+ free(entry);
+ }
+}
+
+/**
+ * Allocate a new poldiff type remap entry, append it to the current
+ * type remap vector, enable it, and return the entry.
+ *
+ * @param diff Policy diff structure containing remap vector.
+ *
+ * @return a new entry, or NULL on error.
+ */
+static poldiff_type_remap_entry_t *poldiff_type_remap_entry_create(poldiff_t * diff)
+{
+ poldiff_type_remap_entry_t *e = NULL;
+ if ((e = calloc(1, sizeof(*e))) == NULL ||
+ (e->orig_types = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (e->mod_types = apol_vector_create_with_capacity(1, free)) == NULL || apol_vector_append(diff->type_map->remap, e) < 0)
+ {
+ type_remap_entry_free(e);
+ return NULL;
+ }
+ diff->remapped = 1;
+ e->enabled = 1;
+ return e;
+}
+
+int poldiff_type_remap_create(poldiff_t * diff, const apol_vector_t * orig_names, const apol_vector_t * mod_names)
+{
+ poldiff_type_remap_entry_t *entry = NULL;
+ size_t i;
+ char *name;
+ const qpol_type_t *type;
+ unsigned char isalias, isattr;
+ int retval = -1, error = 0;
+ if (diff == NULL || orig_names == NULL || mod_names == NULL) {
+ error = EINVAL;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (apol_vector_get_size(orig_names) == 0 ||
+ apol_vector_get_size(mod_names) == 0 || (apol_vector_get_size(orig_names) > 1 && apol_vector_get_size(mod_names) > 1)) {
+ error = EINVAL;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if ((entry = calloc(1, sizeof(*entry))) == NULL ||
+ (entry->orig_types = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (entry->mod_types = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = ENOMEM;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(orig_names); i++) {
+ name = (char *)apol_vector_get_element(orig_names, i);
+ if (qpol_policy_get_type_by_name(diff->orig_qpol, name, &type) < 0 ||
+ qpol_type_get_isalias(diff->orig_qpol, type, &isalias) < 0 ||
+ qpol_type_get_isattr(diff->orig_qpol, type, &isattr) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (isalias || isattr) {
+ error = EINVAL;
+ ERR(diff, "%s is not a primary type.", name);
+ goto cleanup;
+ }
+ if ((name = strdup(name)) == NULL || apol_vector_append(entry->orig_types, (void *)name) < 0) {
+ error = ENOMEM;
+ free(name);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ apol_vector_sort_uniquify(entry->orig_types, apol_str_strcmp, NULL);
+ for (i = 0; i < apol_vector_get_size(mod_names); i++) {
+ name = (char *)apol_vector_get_element(mod_names, i);
+ if (qpol_policy_get_type_by_name(diff->mod_qpol, name, &type) < 0 ||
+ qpol_type_get_isalias(diff->mod_qpol, type, &isalias) < 0 ||
+ qpol_type_get_isattr(diff->mod_qpol, type, &isattr) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (isalias || isattr) {
+ error = EINVAL;
+ ERR(diff, "%s is not a primary type.", name);
+ goto cleanup;
+ }
+ if ((name = strdup(name)) == NULL || apol_vector_append(entry->mod_types, (void *)name) < 0) {
+ error = ENOMEM;
+ free(name);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ apol_vector_sort_uniquify(entry->mod_types, apol_str_strcmp, NULL);
+ entry->enabled = 1;
+ if (apol_vector_append(diff->type_map->remap, entry) < 0) {
+ error = ENOMEM;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ retval = 0;
+ diff->remapped = 1;
+ cleanup:
+ if (retval < 0) {
+ type_remap_entry_free(entry);
+ }
+ errno = error;
+ return retval;
+}
+
+apol_vector_t *poldiff_type_remap_get_entries(const poldiff_t * diff)
+{
+ if (diff == NULL || diff->type_map == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->type_map->remap;
+}
+
+void poldiff_type_remap_entry_remove(poldiff_t * diff, poldiff_type_remap_entry_t * entry)
+{
+ size_t idx;
+ if (diff == NULL || entry == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ if (apol_vector_get_index(diff->type_map->remap, entry, NULL, NULL, &idx) < 0) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ apol_vector_remove(diff->type_map->remap, idx);
+ diff->remapped = 1;
+}
+
+apol_vector_t *poldiff_type_remap_entry_get_original_types(const poldiff_t * diff, const poldiff_type_remap_entry_t * entry)
+{
+ if (diff == NULL || entry == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return apol_vector_create_from_vector(entry->orig_types, NULL, NULL, NULL);
+}
+
+apol_vector_t *poldiff_type_remap_entry_get_modified_types(const poldiff_t * diff, const poldiff_type_remap_entry_t * entry)
+{
+ if (diff == NULL || entry == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return apol_vector_create_from_vector(entry->mod_types, NULL, NULL, NULL);
+}
+
+int poldiff_type_remap_entry_get_is_inferred(const poldiff_type_remap_entry_t * entry)
+{
+ if (entry == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return entry->inferred;
+}
+
+int poldiff_type_remap_entry_get_is_enabled(const poldiff_type_remap_entry_t * entry)
+{
+ if (entry == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return entry->enabled;
+}
+
+void poldiff_type_remap_entry_set_enabled(poldiff_type_remap_entry_t * entry, int enabled)
+{
+ if (entry == NULL) {
+ errno = EINVAL;
+ return;
+ }
+ if (enabled) {
+ entry->enabled = 1;
+ } else {
+ entry->enabled = 0;
+ }
+}
+
+type_map_t *type_map_create(void)
+{
+ type_map_t *map = calloc(1, sizeof(*map));
+ if (map == NULL) {
+ return NULL;
+ }
+ if ((map->remap = apol_vector_create(type_remap_entry_free)) == NULL) {
+ type_map_destroy(&map);
+ return NULL;
+ }
+ return map;
+}
+
+void type_map_destroy(type_map_t ** map)
+{
+ if (map != NULL && *map != NULL) {
+ free((*map)->orig_to_pseudo);
+ free((*map)->mod_to_pseudo);
+ apol_vector_destroy(&(*map)->pseudo_to_orig);
+ apol_vector_destroy(&(*map)->pseudo_to_mod);
+ apol_vector_destroy(&(*map)->remap);
+ free(*map);
+ *map = NULL;
+ }
+}
+
+/**
+ * If --enable-debug is given, then dump to stdout the type map from
+ * policy's types -> pseudo-types.
+ */
+static void type_map_dump(poldiff_t * diff)
+{
+#ifdef SETOOLS_DEBUG
+ size_t i, j;
+ apol_vector_t *v;
+ const qpol_type_t *t;
+ const char *name;
+ printf("# type map debug dump (qpol_type_t -> pseudo-type):\norig:\n");
+ for (i = 0; i < diff->type_map->num_orig_types; i++) {
+ printf("%3zd:%5d", i, diff->type_map->orig_to_pseudo[i]);
+ if ((i + 1) % 5 == 0) {
+ printf("\n");
+ } else {
+ printf("\t");
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(diff->type_map->pseudo_to_orig); i++) {
+ v = apol_vector_get_element(diff->type_map->pseudo_to_orig, i);
+ printf("\n%3zd->", i);
+ for (j = 0; j < apol_vector_get_size(v); j++) {
+ t = apol_vector_get_element(v, j);
+ qpol_type_get_name(diff->orig_qpol, t, &name);
+ printf(" %s", name);
+ }
+ }
+ printf("\nmod:\n");
+ for (i = 0; i < diff->type_map->num_mod_types; i++) {
+ printf("%3zd:%5d", i, diff->type_map->mod_to_pseudo[i]);
+ if ((i + 1) % 5 == 0) {
+ printf("\n");
+ } else {
+ printf("\t");
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(diff->type_map->pseudo_to_mod); i++) {
+ v = apol_vector_get_element(diff->type_map->pseudo_to_mod, i);
+ printf("\n%3zd->", i);
+ for (j = 0; j < apol_vector_get_size(v); j++) {
+ t = apol_vector_get_element(v, j);
+ qpol_type_get_name(diff->mod_qpol, t, &name);
+ printf(" %s", name);
+ }
+ }
+ printf("\n");
+#endif
+}
+
+/**
+ * Free a vector of qpol_type_t pointers.
+ */
+static void type_map_vector_free(void *elem)
+{
+ apol_vector_t *v = (apol_vector_t *) elem;
+ if (v != NULL) {
+ apol_vector_destroy(&v);
+ }
+}
+
+int type_map_build(poldiff_t * diff)
+{
+ type_map_t *map;
+ apol_vector_t *ov = NULL, *mv = NULL;
+ int retval = -1, error = 0;
+ size_t i, j;
+ const qpol_type_t *t;
+ uint32_t val, max_val, next_val;
+ apol_vector_t *reverse_v = NULL;
+
+ map = diff->type_map;
+ free(map->orig_to_pseudo);
+ map->orig_to_pseudo = NULL;
+ map->num_orig_types = 0;
+ free(map->mod_to_pseudo);
+ map->mod_to_pseudo = NULL;
+ map->num_mod_types = 0;
+ apol_vector_destroy(&map->pseudo_to_orig);
+ apol_vector_destroy(&map->pseudo_to_mod);
+
+ if (apol_type_get_by_query(diff->orig_pol, NULL, &ov) < 0 || apol_type_get_by_query(diff->mod_pol, NULL, &mv) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+
+ /* there is no guarantee that the number of types is equal to
+ * the highest type value (because a policy could have
+ * attributes), so calculate them here */
+ max_val = 0;
+ for (i = 0; i < apol_vector_get_size(ov); i++) {
+ t = (qpol_type_t *) apol_vector_get_element(ov, i);
+ if (qpol_type_get_value(diff->orig_qpol, t, &val) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (val > max_val) {
+ max_val = val;
+ }
+ }
+ if ((map->orig_to_pseudo = calloc(max_val, sizeof(*(map->orig_to_pseudo)))) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ map->num_orig_types = max_val;
+ max_val = 0;
+ for (i = 0; i < apol_vector_get_size(mv); i++) {
+ t = (qpol_type_t *) apol_vector_get_element(mv, i);
+ if (qpol_type_get_value(diff->mod_qpol, t, &val) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (val > max_val) {
+ max_val = val;
+ }
+ }
+ if ((map->mod_to_pseudo = calloc(max_val, sizeof(*(map->mod_to_pseudo)))) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ map->num_mod_types = max_val;
+
+ if ((map->pseudo_to_orig = apol_vector_create(type_map_vector_free)) == NULL
+ || (map->pseudo_to_mod = apol_vector_create(type_map_vector_free)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ next_val = 1;
+ for (i = 0; i < apol_vector_get_size(map->remap); i++) {
+ poldiff_type_remap_entry_t *e;
+ const char *name;
+ e = (poldiff_type_remap_entry_t *) apol_vector_get_element(map->remap, i);
+ if (!e->enabled) {
+ continue;
+ }
+
+ if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (j = 0; j < apol_vector_get_size(e->orig_types); j++) {
+ name = (const char *)apol_vector_get_element(e->orig_types, j);
+ if (qpol_policy_get_type_by_name(diff->orig_qpol, name, &t) < 0 ||
+ qpol_type_get_value(diff->orig_qpol, t, &val) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (map->orig_to_pseudo[val - 1] != 0) {
+ error = EINVAL;
+ ERR(diff, "Type %s is already remapped.", name);
+ goto cleanup;
+ }
+ map->orig_to_pseudo[val - 1] = next_val;
+ if (apol_vector_append(reverse_v, (void *)t) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_append(map->pseudo_to_orig, reverse_v) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ reverse_v = NULL;
+
+ if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (j = 0; j < apol_vector_get_size(e->mod_types); j++) {
+ name = (const char *)apol_vector_get_element(e->mod_types, j);
+ if (qpol_policy_get_type_by_name(diff->mod_qpol, name, &t) < 0 ||
+ qpol_type_get_value(diff->mod_qpol, t, &val) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (map->mod_to_pseudo[val - 1] != 0) {
+ error = EINVAL;
+ ERR(diff, "Type %s is already remapped.", name);
+ goto cleanup;
+ }
+ map->mod_to_pseudo[val - 1] = next_val;
+ if (apol_vector_append(reverse_v, (void *)t) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_append(map->pseudo_to_mod, reverse_v) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ reverse_v = NULL;
+
+ next_val++;
+ }
+
+ /* all remaining types (both from orig and mod) get their own
+ * values */
+ for (i = 0; i < apol_vector_get_size(ov); i++) {
+ t = apol_vector_get_element(ov, i);
+ if (qpol_type_get_value(diff->orig_qpol, t, &val) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (map->orig_to_pseudo[val - 1] == 0) {
+ map->orig_to_pseudo[val - 1] = next_val;
+ if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ apol_vector_append(reverse_v, (void *)t) < 0 ||
+ apol_vector_append(map->pseudo_to_orig, reverse_v) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ reverse_v = NULL;
+ if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ apol_vector_append(map->pseudo_to_mod, reverse_v) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ reverse_v = NULL;
+ next_val++;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(mv); i++) {
+ t = apol_vector_get_element(mv, i);
+ if (qpol_type_get_value(diff->mod_qpol, t, &val) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (map->mod_to_pseudo[val - 1] == 0) {
+ map->mod_to_pseudo[val - 1] = next_val;
+ if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ apol_vector_append(reverse_v, (void *)t) < 0 || apol_vector_append(map->pseudo_to_mod, reverse_v) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ reverse_v = NULL;
+ if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ apol_vector_append(map->pseudo_to_orig, reverse_v) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ reverse_v = NULL;
+ next_val++;
+ }
+ }
+
+ type_map_dump(diff);
+
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&ov);
+ apol_vector_destroy(&mv);
+ apol_vector_destroy(&reverse_v);
+ error = errno;
+ return retval;
+}
+
+void poldiff_type_remap_flush(poldiff_t * diff)
+{
+ if (diff == NULL || diff->type_map == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ apol_vector_destroy(&(diff->type_map->remap));
+ /* no error checking below */
+ diff->type_map->remap = apol_vector_create(type_remap_entry_free);
+ diff->remapped = 1;
+}
+
+/**
+ * Convenience struct for comparing elements within arrays of primary types.
+ */
+struct type_map_comp
+{
+ poldiff_t *diff;
+ /** from which policy the first element came, either
+ * POLDIFF_POLICY_ORIG or POLDIFF_POLICY_MOD */
+ int dir;
+};
+
+/**
+ * Given two qpol_type_t pointers, both of which are primary types,
+ * compare their names for equivalence.
+ *
+ * @param a Pointer to a qpol_type_t from a policy.
+ * @param b Pointer to a qpol_type_t from a policy.
+ * @param data Pointer to a type_map_comp struct.
+ *
+ * @return 0 if the names match, non-zero if not.
+ */
+static int type_map_primary_comp(const void *a, const void *b, void *data)
+{
+ const qpol_type_t *ta = a;
+ const qpol_type_t *tb = b;
+ struct type_map_comp *c = (struct type_map_comp *)data;
+ poldiff_t *diff = c->diff;
+ int dir = c->dir;
+ const char *na, *nb;
+ if (dir == POLDIFF_POLICY_ORIG) {
+ if (qpol_type_get_name(diff->orig_qpol, ta, &na) < 0 || qpol_type_get_name(diff->mod_qpol, tb, &nb) < 0) {
+ return -1;
+ }
+ } else {
+ if (qpol_type_get_name(diff->mod_qpol, ta, &na) < 0 || qpol_type_get_name(diff->orig_qpol, tb, &nb) < 0) {
+ return -1;
+ }
+ }
+ return strcmp(na, nb);
+}
+
+/**
+ * Given two qpol_type_t pointers, both of which are primary types,
+ * see if the first type matches any of the other type's aliases.
+ *
+ * @param a Pointer to a qpol_type_t from a policy.
+ * @param b Pointer to a qpol_type_t from a policy.
+ * @param data Pointer to a type_map_comp struct.
+ *
+ * @return 0 if b is a member of a's aliases, non-zero if not.
+ */
+static int type_map_prim_alias_comp(const void *a, const void *b, void *data)
+{
+ const qpol_type_t *ta = a;
+ const qpol_type_t *tb = b;
+ struct type_map_comp *c = (struct type_map_comp *)data;
+ poldiff_t *diff = c->diff;
+ int dir = c->dir;
+ const char *prim, *alias;
+ qpol_iterator_t *iter = NULL;
+ if (dir == POLDIFF_POLICY_ORIG) {
+ if (qpol_type_get_alias_iter(diff->orig_qpol, ta, &iter) < 0 || qpol_type_get_name(diff->mod_qpol, tb, &prim) < 0) {
+ qpol_iterator_destroy(&iter);
+ return -1;
+ }
+ } else {
+ if (qpol_type_get_alias_iter(diff->mod_qpol, ta, &iter) < 0 || qpol_type_get_name(diff->orig_qpol, tb, &prim) < 0) {
+ qpol_iterator_destroy(&iter);
+ return -1;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&alias) < 0) {
+ qpol_iterator_destroy(&iter);
+ return -1;
+ }
+ if (strcmp(prim, alias) == 0) {
+ qpol_iterator_destroy(&iter);
+ return 0;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ return -1;
+}
+
+/**
+ * Given two qpol_type_t pointers, both of which are primary types,
+ * see if the first type's aliases matches the second type's aliases.
+ *
+ * @param a Pointer to a qpol_type_t from a policy.
+ * @param b Pointer to a qpol_type_t from a policy.
+ * @param data Pointer to a type_map_comp struct.
+ *
+ * @return 0 if b is a member of a's aliases, non-zero if not.
+ */
+static int type_map_prim_aliases_comp(const void *a, const void *b, void *data)
+{
+ qpol_type_t *ta = (qpol_type_t *) a;
+ qpol_type_t *tb = (qpol_type_t *) b;
+ struct type_map_comp *c = (struct type_map_comp *)data;
+ poldiff_t *diff = c->diff;
+ int dir = c->dir;
+ qpol_policy_t *p1, *p2;
+ qpol_iterator_t *iter1 = NULL, *iter2 = NULL;
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ size_t i;
+ int retval = -1, error = 0;
+ if (dir == POLDIFF_POLICY_ORIG) {
+ p1 = diff->orig_qpol;
+ p2 = diff->mod_qpol;
+ } else {
+ p1 = diff->mod_qpol;
+ p2 = diff->orig_qpol;
+ }
+ if (qpol_type_get_alias_iter(p1, ta, &iter1) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((v1 = apol_vector_create_from_iter(iter1, NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (qpol_type_get_alias_iter(p2, tb, &iter2) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((v2 = apol_vector_create_from_iter(iter2, NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) {
+ retval = 1;
+ goto cleanup;
+ } else {
+ apol_vector_sort_uniquify(v1, apol_str_strcmp, NULL);
+ apol_vector_sort_uniquify(v2, apol_str_strcmp, NULL);
+ retval = apol_vector_compare(v1, v2, apol_str_strcmp, NULL, &i);
+ }
+ cleanup:
+ qpol_iterator_destroy(&iter1);
+ qpol_iterator_destroy(&iter2);
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ errno = error;
+ return retval;
+}
+
+/**
+ * If --enable-debug is given, then dump to stdout the type-map from
+ * pseudo-types to the policy's type(s).
+ */
+static void type_remap_vector_dump(poldiff_t * diff)
+{
+#ifdef SETOOLS_DEBUG
+ apol_vector_t *v, *w;
+ size_t i, j;
+ poldiff_type_remap_entry_t *e;
+ char *name;
+ printf("# type remap vector debug dump (pseudo-type -> qpol_type_t(s):\n");
+ v = poldiff_type_remap_get_entries(diff);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ e = apol_vector_get_element(v, i);
+ printf("%zd\t%s\t", i, poldiff_type_remap_entry_get_is_enabled(e) ? "en" : "dis");
+ w = poldiff_type_remap_entry_get_original_types(diff, e);
+ for (j = 0; j < apol_vector_get_size(w); j++) {
+ name = apol_vector_get_element(w, j);
+ printf("%s ", name);
+ }
+ apol_vector_destroy(&w);
+ printf("-> ");
+ w = poldiff_type_remap_entry_get_modified_types(diff, e);
+ for (j = 0; j < apol_vector_get_size(w); j++) {
+ name = apol_vector_get_element(w, j);
+ printf("%s ", name);
+ }
+ apol_vector_destroy(&w);
+ printf("\n");
+ }
+#endif
+}
+
+static int type_map_entry_append_qtypes(poldiff_t * diff, poldiff_type_remap_entry_t * entry, const qpol_type_t * t,
+ const qpol_type_t * u)
+{
+ const char *name;
+ char *dup_name;
+ if (qpol_type_get_name(diff->orig_qpol, t, &name) < 0) {
+ return -1;
+ }
+ if ((dup_name = strdup(name)) == NULL || apol_vector_append(entry->orig_types, (void *)dup_name) < 0) {
+ free(dup_name);
+ return -1;
+ }
+
+ if (qpol_type_get_name(diff->mod_qpol, u, &name) < 0) {
+ return -1;
+ }
+ if ((dup_name = strdup(name)) == NULL || apol_vector_append(entry->mod_types, (void *)dup_name) < 0) {
+ free(dup_name);
+ return -1;
+ }
+ return 0;
+}
+
+int type_map_infer(poldiff_t * diff)
+{
+ apol_vector_t *ov = NULL, *mv = NULL;
+ char *orig_done = NULL, *mod_done = NULL;
+ size_t num_orig, num_mod, i, j;
+ qpol_type_t *t, *u;
+ struct type_map_comp c = { diff, 0 };
+ poldiff_type_remap_entry_t *entry = NULL;
+ int retval = -1, error = 0;
+
+ INFO(diff, "%s", "Inferring type remap.");
+ if (apol_type_get_by_query(diff->orig_pol, NULL, &ov) < 0 || apol_type_get_by_query(diff->mod_pol, NULL, &mv) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ num_orig = apol_vector_get_size(ov);
+ num_mod = apol_vector_get_size(mv);
+ if ((orig_done = calloc(1, num_orig)) == NULL || (mod_done = calloc(1, num_mod)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ /* first map primary <--> primary */
+ c.dir = POLDIFF_POLICY_MOD;
+ for (i = 0; i < num_orig; i++) {
+ t = (qpol_type_t *) apol_vector_get_element(ov, i);
+ if (apol_vector_get_index(mv, t, type_map_primary_comp, &c, &j) < 0) {
+ continue;
+ }
+ assert(!mod_done[j]);
+ u = (qpol_type_t *) apol_vector_get_element(mv, j);
+ if ((entry = poldiff_type_remap_entry_create(diff)) == NULL || type_map_entry_append_qtypes(diff, entry, t, u) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ entry->inferred = 1;
+ orig_done[i] = 1;
+ mod_done[j] = 1;
+ }
+
+ /* now map primary -> primary's alias */
+ c.dir = POLDIFF_POLICY_MOD;
+ for (i = 0; i < num_orig; i++) {
+ if (orig_done[i]) {
+ continue;
+ }
+ t = (qpol_type_t *) apol_vector_get_element(ov, i);
+ u = NULL;
+ for (j = 0; j < num_mod; j++) {
+ if (mod_done[j]) {
+ continue;
+ }
+ u = (qpol_type_t *) apol_vector_get_element(mv, j);
+ if (type_map_prim_alias_comp(u, t, &c) == 0) {
+ break;
+ }
+ }
+ if (j >= num_mod) {
+ continue;
+ }
+ if ((entry = poldiff_type_remap_entry_create(diff)) == NULL || type_map_entry_append_qtypes(diff, entry, t, u) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ entry->inferred = 1;
+ orig_done[i] = 1;
+ mod_done[j] = 1;
+ }
+
+ /* then map primary's alias <- primary */
+ c.dir = POLDIFF_POLICY_ORIG;
+ for (j = 0; j < num_mod; j++) {
+ if (mod_done[j]) {
+ continue;
+ }
+ u = (qpol_type_t *) apol_vector_get_element(mv, j);
+ t = NULL;
+ for (i = 0; i < num_orig; i++) {
+ if (orig_done[i]) {
+ continue;
+ }
+ t = (qpol_type_t *) apol_vector_get_element(ov, i);
+ if (type_map_prim_alias_comp(t, u, &c) == 0) {
+ break;
+ }
+ }
+ if (i >= num_orig) {
+ continue;
+ }
+ if ((entry = poldiff_type_remap_entry_create(diff)) == NULL || type_map_entry_append_qtypes(diff, entry, t, u) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ entry->inferred = 1;
+ orig_done[i] = 1;
+ mod_done[j] = 1;
+ }
+
+ /* map alias <-> alias */
+ c.dir = POLDIFF_POLICY_MOD;
+ for (i = 0; i < num_orig; i++) {
+ if (orig_done[i]) {
+ continue;
+ }
+ t = (qpol_type_t *) apol_vector_get_element(ov, i);
+ u = NULL;
+ for (j = 0; j < num_mod; j++) {
+ if (mod_done[j]) {
+ continue;
+ }
+ u = (qpol_type_t *) apol_vector_get_element(mv, j);
+ if (type_map_prim_aliases_comp(u, t, &c) == 0) {
+ break;
+ }
+ }
+ if (j >= num_mod) {
+ continue;
+ }
+ if ((entry = poldiff_type_remap_entry_create(diff)) == NULL || type_map_entry_append_qtypes(diff, entry, t, u) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ entry->inferred = 1;
+ orig_done[i] = 1;
+ mod_done[j] = 1;
+ }
+
+ type_remap_vector_dump(diff);
+
+ retval = 0;
+ diff->remapped = 1;
+ cleanup:
+ apol_vector_destroy(&ov);
+ apol_vector_destroy(&mv);
+ free(orig_done);
+ free(mod_done);
+ errno = error;
+ return retval;
+}
+
+uint32_t type_map_lookup(const poldiff_t * diff, const qpol_type_t * type, int which_pol)
+{
+ uint32_t val;
+ if (which_pol == POLDIFF_POLICY_ORIG) {
+ if (qpol_type_get_value(diff->orig_qpol, type, &val) < 0) {
+ return 0;
+ }
+ assert(val <= diff->type_map->num_orig_types);
+ assert(diff->type_map->orig_to_pseudo[val - 1] != 0);
+ return diff->type_map->orig_to_pseudo[val - 1];
+ } else {
+ if (qpol_type_get_value(diff->mod_qpol, type, &val) < 0) {
+ return 0;
+ }
+ assert(val <= diff->type_map->num_mod_types);
+ assert(diff->type_map->mod_to_pseudo[val - 1] != 0);
+ return diff->type_map->mod_to_pseudo[val - 1];
+ }
+}
+
+const apol_vector_t *type_map_lookup_reverse(const poldiff_t * diff, uint32_t val, int which_pol)
+{
+ if (which_pol == POLDIFF_POLICY_ORIG) {
+ return apol_vector_get_element(diff->type_map->pseudo_to_orig, val - 1);
+ } else {
+ return apol_vector_get_element(diff->type_map->pseudo_to_mod, val - 1);
+ }
+}
+
+const char *type_map_get_name(const poldiff_t * diff, const uint32_t pseudo_val, int pol)
+{
+ const apol_vector_t *v = NULL;
+ const char *name = NULL;
+ const qpol_type_t *t;
+
+ v = type_map_lookup_reverse(diff, pseudo_val, pol);
+ if (apol_vector_get_size(v) == 0) {
+ return NULL;
+ }
+ t = apol_vector_get_element(v, 0);
+ if (pol == POLDIFF_POLICY_ORIG)
+ qpol_type_get_name(diff->orig_qpol, t, &name);
+ else
+ qpol_type_get_name(diff->mod_qpol, t, &name);
+ return name;
+}
diff --git a/libpoldiff/src/type_map_internal.h b/libpoldiff/src/type_map_internal.h
new file mode 100644
index 0000000..786f38d
--- /dev/null
+++ b/libpoldiff/src/type_map_internal.h
@@ -0,0 +1,171 @@
+/**
+ * @file
+ * Protected interface for type equivalence mapping for semantic
+ * difference calculations.
+ *
+ * @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 POLDIFF_TYPE_MAP_INTERNAL_H
+#define POLDIFF_TYPE_MAP_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <qpol/policy.h>
+
+ typedef struct type_map type_map_t;
+
+#define POLDIFF_POLICY_ORIG 1
+#define POLDIFF_POLICY_MOD 2
+
+/**
+ * Allocate and return a new type_map_t object.
+ *
+ * @return a new type map object. The caller must call
+ * type_map_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ type_map_t *type_map_create(void);
+
+/**
+ * Free all memory used by the type map.
+ *
+ * @param map Reference pointer to the type map to destroy. This
+ * pointer will be set to NULL afterwards.
+ */
+ void type_map_destroy(type_map_t ** map);
+
+/**
+ * Build the type map for a policy difference structure, using all
+ * enabled poldiff_type_remap_entry entries as hints for the
+ * mappings. This function should be called by poldiff_run() before
+ * each run.
+ *
+ * @param diff The policy difference structure containing the
+ * policies from which to construct the type map.
+ * @return 0 on success and < 0 on error, if the call fails, errno will
+ * be set and the policy difference structure will be unchanged.
+ */
+ int type_map_build(poldiff_t * diff);
+
+/**
+ * Clear away all type remap entries within the type map. This
+ * function should be called some time after type_map_create().
+ *
+ * @param diff The policy difference structure containing the
+ * policies from which to construct the type map.
+ */
+ void poldiff_type_remap_flush(poldiff_t * diff);
+
+/**
+ * Infer type remappings and append them to the current type remap
+ * vector. The vector should probably be first flushed via
+ * poldiff_type_remap_flush(). Generated entries will have their
+ * 'inferred' and 'enabled' flags set.
+ *
+ * The heuristic for determining type remaps is as follow.
+ * <ol>
+ *
+ * <li>If any type name exists as a primary in both policies then map
+ * it.
+ *
+ * <li>For all remaining unmapped primary types in the original
+ * policy, if that type name appears as an alias to an unmapped
+ * primary in the modified then map it.
+ *
+ * <li>For all remaining unmapped primary types in the modified
+ * policy, if that type name appears as an alias to an unmapped
+ * primary in the original then map it.
+ *
+ * <li>For all remaining unmapped primary types in both policies, if
+ * all of the aliases of one type are exactly the same as another
+ * type's aliases then map it.
+ *
+ * <li>All remaining types are left as unmapped.
+ *
+ * </ol>
+ *
+ * A side-effect of this heuristic is that it is reversible; the same
+ * inferences are made regardless of the order of policies.
+ *
+ * @param diff The policy difference structure containing the
+ * policies from which to construct the type map.
+ *
+ * @return 0 on success, < 0 on error and errno will be set.
+ */
+ int type_map_infer(poldiff_t * diff);
+
+/**
+ * Given a qpol_type_t and a flag indicating from which the policy
+ * the type originated, return its remapped value. (type_map_build()
+ * must have been first called.)
+ *
+ * @param diff The policy difference structure assocated with the
+ * types.
+ * @param type Type to lookup.
+ * @param which_pol One of POLDIFF_POLICY_ORIG or POLDIFF_POLICY_MOD.
+ *
+ * @return The type's remapped value. On error this will be 0 and
+ * errno will be set.
+ */
+ uint32_t type_map_lookup(const poldiff_t * diff, const qpol_type_t * type, int which_pol);
+
+/**
+ * Given a pseudo-type's value and a flag indicating for which policy
+ * to look up, return a vector of qpol_type_t pointers to reference
+ * back to the unmapped types. (type_map_build() must have been
+ * first called.) Note that the returned vector could be empty for
+ * the situation where a type was added or removed.
+ *
+ * @param diff The policy difference structure assocated with the
+ * types.
+ * @param val Pseudo-type value to lookup.
+ * @param which_pol One of POLDIFF_POLICY_ORIG or POLDIFF_POLICY_MOD.
+ *
+ * @return A vector of qpol_type_t pointers. The caller should not
+ * free this vector. If the call fails, NULL will be returned and
+ * errno will be set.
+ */
+ const apol_vector_t *type_map_lookup_reverse(const poldiff_t * diff, uint32_t val, int which_pol);
+
+/**
+ * Get the first name that can be found for a pseudo type value.
+ *
+ * @param diff Policy difference structure associated with the value.
+ * @param pseudo_val Value for which to get a name.
+ * @param pol The policy to use, either POLDIFF_POLICY_ORIG or
+ * POLDIFF_POLICY_MOD.
+ *
+ * @return A valid name of a type from either policy that maps to the
+ * specified value. If the type does not exist then return NULL. Do
+ * not modify the string; it is a pointer into the policy's symbol
+ * table.
+ */
+ const char *type_map_get_name(const poldiff_t * diff, const uint32_t pseudo_val, int pol);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_TYPE_MAP_INTERNAL_H */
diff --git a/libpoldiff/src/user_diff.c b/libpoldiff/src/user_diff.c
new file mode 100644
index 0000000..e673291
--- /dev/null
+++ b/libpoldiff/src/user_diff.c
@@ -0,0 +1,789 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in users.
+ *
+ * @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 "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_user_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_user
+{
+ char *name;
+ poldiff_form_e form;
+ /* the next three are vector of strings */
+ apol_vector_t *unmodified_roles;
+ apol_vector_t *added_roles;
+ apol_vector_t *removed_roles;
+ /** if not diffing a MLS policy, this will be NULL */
+ poldiff_level_t *orig_default_level;
+ /** if not diffing a MLS policy, this will be NULL; this is
+ also NULL if orig_default_level->form is
+ POLDIFF_FORM_MODIFIED */
+ poldiff_level_t *mod_default_level;
+ /** if not diffing MLS policies then the range is NULL */
+ poldiff_range_t *range;
+};
+
+void poldiff_user_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->user_diffs->num_added;
+ stats[1] = diff->user_diffs->num_removed;
+ stats[2] = diff->user_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+/**
+ * Generate the to_string for a modified user.
+ */
+static char *user_to_modified_string(const poldiff_t * diff, const poldiff_user_t * u)
+{
+ size_t len = 0, i;
+ char *s = NULL, *t = NULL, *role, *range = NULL;
+ size_t num_added_roles = apol_vector_get_size(u->added_roles);
+ size_t num_removed_roles = apol_vector_get_size(u->removed_roles);
+ if (apol_str_appendf(&s, &len, "* %s\n", u->name) < 0) {
+ goto err;
+ }
+ if (num_added_roles > 0 || num_removed_roles > 0) {
+ if (apol_str_append(&s, &len, " roles {") < 0) {
+ goto err;
+ }
+ for (i = 0; i < apol_vector_get_size(u->unmodified_roles); i++) {
+ role = (char *)apol_vector_get_element(u->unmodified_roles, i);
+ if (apol_str_appendf(&s, &len, " %s", role) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < num_added_roles; i++) {
+ role = (char *)apol_vector_get_element(u->added_roles, i);
+ if (apol_str_appendf(&s, &len, " +%s", role) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < num_removed_roles; i++) {
+ role = (char *)apol_vector_get_element(u->removed_roles, i);
+ if (apol_str_appendf(&s, &len, " -%s", role) < 0) {
+ goto err;
+ }
+ }
+ if (apol_str_append(&s, &len, " }\n") < 0) {
+ goto err;
+ }
+ }
+ if ((u->mod_default_level != NULL || u->orig_default_level != NULL) && apol_str_append(&s, &len, " level:\n") < 0) {
+ goto err;
+ }
+ if (u->mod_default_level != NULL) {
+ if ((t = poldiff_level_to_string_brief(diff, u->mod_default_level)) == NULL) {
+ goto err;
+ }
+ if (apol_str_appendf(&s, &len, " %s", t) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto err;
+ }
+ free(t);
+ t = NULL;
+ }
+ if (u->orig_default_level != NULL) {
+ if ((t = poldiff_level_to_string_brief(diff, u->orig_default_level)) == NULL) {
+ goto err;
+ }
+ if (apol_str_appendf(&s, &len, " %s", t) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto err;
+ }
+ free(t);
+ t = NULL;
+ }
+ if (u->range != NULL) {
+ if ((range = poldiff_range_to_string_brief(diff, u->range)) == NULL
+ || (apol_str_appendf(&s, &len, "%s", range) < 0)) {
+ free(range);
+ goto err;
+ }
+ free(range);
+ }
+ return s;
+ err:
+ free(s);
+ free(t);
+ return NULL;
+}
+
+char *poldiff_user_to_string(const poldiff_t * diff, const void *user)
+{
+ poldiff_user_t *u = (poldiff_user_t *) user;
+ size_t len = 0;
+ char *s = NULL;
+ if (diff == NULL || user == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (u->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", u->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", u->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if ((s = user_to_modified_string(diff, u)) == NULL) {
+ goto err;
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ err:
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_user_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->user_diffs->diffs;
+}
+
+const char *poldiff_user_get_name(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->name;
+}
+
+poldiff_form_e poldiff_user_get_form(const void *user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_user_t *)user)->form;
+}
+
+const apol_vector_t *poldiff_user_get_unmodified_roles(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->unmodified_roles;
+}
+
+const apol_vector_t *poldiff_user_get_added_roles(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->added_roles;
+}
+
+const apol_vector_t *poldiff_user_get_removed_roles(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->removed_roles;
+}
+
+const poldiff_level_t *poldiff_user_get_original_dfltlevel(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->orig_default_level;
+}
+
+const poldiff_level_t *poldiff_user_get_modified_dfltlevel(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->mod_default_level;
+}
+
+const poldiff_range_t *poldiff_user_get_range(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->range;
+}
+
+/*************** protected functions for users ***************/
+
+static void user_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_user_t *u = (poldiff_user_t *) elem;
+ free(u->name);
+ apol_vector_destroy(&u->added_roles);
+ apol_vector_destroy(&u->removed_roles);
+ apol_vector_destroy(&u->unmodified_roles);
+ level_free(u->orig_default_level);
+ level_free(u->mod_default_level);
+ range_destroy(&u->range);
+ free(u);
+ }
+}
+
+poldiff_user_summary_t *user_create(void)
+{
+ poldiff_user_summary_t *us = calloc(1, sizeof(*us));
+ if (us == NULL) {
+ return NULL;
+ }
+ if ((us->diffs = apol_vector_create(user_free)) == NULL) {
+ user_destroy(&us);
+ return NULL;
+ }
+ return us;
+}
+
+void user_destroy(poldiff_user_summary_t ** us)
+{
+ if (us != NULL && *us != NULL) {
+ apol_vector_destroy(&(*us)->diffs);
+ free(*us);
+ *us = NULL;
+ }
+}
+
+int user_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ user_destroy(&diff->user_diffs);
+ diff->user_diffs = user_create();
+ if (diff->user_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two users from the same policy.
+ */
+static int user_name_comp(const void *x, const void *y, void *arg)
+{
+ const qpol_user_t *u1 = x;
+ const qpol_user_t *u2 = y;
+ apol_policy_t *p = (apol_policy_t *) arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+ if (qpol_user_get_name(q, u1, &name1) < 0 || qpol_user_get_name(q, u2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *user_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_user_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create_from_iter(iter, NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, user_name_comp, (void *)policy);
+ return v;
+}
+
+int user_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ const qpol_user_t *u1 = x;
+ const qpol_user_t *u2 = y;
+ const char *name1, *name2;
+ if (qpol_user_get_name(diff->orig_qpol, u1, &name1) < 0 || qpol_user_get_name(diff->mod_qpol, u2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new user difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the user that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling user_free() upon the returned
+ * value.
+ */
+static poldiff_user_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_user_t *pu;
+ int error;
+ if ((pu = calloc(1, sizeof(*pu))) == NULL ||
+ (pu->name = strdup(name)) == NULL ||
+ (pu->added_roles = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pu->removed_roles = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pu->unmodified_roles = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = errno;
+ user_free(pu);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pu->form = form;
+ return pu;
+}
+
+/**
+ * Given a user, return a vector of its allowed roles (in the form of
+ * strings).
+ *
+ * @param diff Policy diff error handler.
+ * @param p Policy from which the user came.
+ * @param user User whose roles to get.
+ *
+ * @return Vector of role strings for the user. The caller is
+ * responsible for calling apol_vector_destroy(). On error, return
+ * NULL.
+ */
+static apol_vector_t *user_get_roles(const poldiff_t * diff, const apol_policy_t * p, const qpol_user_t * user)
+{
+ qpol_iterator_t *iter = NULL;
+ const qpol_role_t *role;
+ const char *role_name;
+ char *role_name2;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1, error = 0;
+
+ if ((v = apol_vector_create(free)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_user_get_role_iter(q, user, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&role) < 0 || qpol_role_get_name(q, role, &role_name)) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((role_name2 = strdup(role_name)) == NULL || apol_vector_append(v, (void *)role_name2) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ free(role_name2);
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+/**
+ * Perform a deep diff of the default MLS levels assigned to the two
+ * users.
+ *
+ * @param diff Diff structure containing the original and modified
+ * policies.
+ * @param u1 User from original policy to examine, or NULL if the user
+ * was added.
+ * @param u2 User from modified policy to examine, or NULL if the user
+ * was removed.
+ * @param u Result structure where differences are to be recorded.
+ *
+ * @return Greater than zero if a diff was found, zero if none found,
+ * less than zero for errors.
+ */
+static int user_deep_diff_default_levels(poldiff_t * diff, const qpol_user_t * u1, const qpol_user_t * u2, poldiff_user_t * u)
+{
+ const qpol_mls_level_t *ql1 = NULL, *ql2 = NULL;
+ poldiff_level_t *pl = NULL;
+ apol_mls_level_t *l1 = NULL, *l2 = NULL;
+ int retval = -1;
+ if (u1 != NULL && qpol_user_get_dfltlevel(diff->orig_qpol, u1, &ql1) < 0) {
+ return -1;
+ }
+ if (u2 != NULL && qpol_user_get_dfltlevel(diff->mod_qpol, u2, &ql2) < 0) {
+ return -1;
+ }
+ if (ql1 == NULL && ql2 == NULL) {
+ /* neither policy is MLS */
+ return 0;
+ }
+ if (ql1 == NULL) {
+ if ((l2 = apol_mls_level_create_from_qpol_mls_level(diff->mod_pol, ql2)) == NULL ||
+ (pl = level_create_from_apol_mls_level(l2, POLDIFF_FORM_ADDED)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ u->mod_default_level = pl;
+ retval = 1;
+ } else if (ql2 == NULL) {
+ if ((l1 = apol_mls_level_create_from_qpol_mls_level(diff->orig_pol, ql1)) == NULL ||
+ (pl = level_create_from_apol_mls_level(l1, POLDIFF_FORM_REMOVED)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ u->orig_default_level = pl;
+ retval = 1;
+ } else {
+ if ((l1 = apol_mls_level_create_from_qpol_mls_level(diff->orig_pol, ql1)) == NULL ||
+ (l2 = apol_mls_level_create_from_qpol_mls_level(diff->mod_pol, ql2)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (level_deep_diff_apol_mls_levels(diff, l1, l2, &u->orig_default_level, &u->mod_default_level) < 0) {
+ goto cleanup;
+ }
+ if (u->orig_default_level != NULL) {
+ retval = 1;
+ }
+ }
+ if (retval == -1) {
+ /* if reach this point, then no differences were found */
+ retval = 0;
+ }
+ cleanup:
+ apol_mls_level_destroy(&l1);
+ apol_mls_level_destroy(&l2);
+ if (retval < 0) {
+ level_free(pl);
+ }
+ return retval;
+}
+
+/**
+ * Perform a deep diff of the MLS ranges assigned to the two users.
+ *
+ * @param diff Diff structure containing the original and modified
+ * policies.
+ * @param u1 User from original policy to examine, or NULL if the user
+ * was added.
+ * @param u2 User from modified policy to examine, or NULL if the user
+ * was removed.
+ * @param u Result structure where differences are to be recorded.
+ *
+ * @return Greater than zero if a diff was found, zero if none found,
+ * less than zero for errors.
+ */
+static int user_deep_diff_ranges(poldiff_t * diff, const qpol_user_t * u1, const qpol_user_t * u2, poldiff_user_t * u)
+{
+ const qpol_mls_range_t *r1 = NULL, *r2 = NULL;
+ poldiff_range_t *pr = NULL;
+ int retval = -1;
+ if (u1 != NULL && qpol_user_get_range(diff->orig_qpol, u1, &r1) < 0) {
+ return -1;
+ }
+ if (u2 != NULL && qpol_user_get_range(diff->mod_qpol, u2, &r2) < 0) {
+ return -1;
+ }
+ if (r1 == NULL && r2 == NULL) {
+ /* neither policy is MLS */
+ return 0;
+ }
+ if (r1 == NULL) {
+ if ((pr = range_create(diff, r1, r2, POLDIFF_FORM_ADDED)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ u->range = pr;
+ pr = NULL;
+ retval = 1;
+ } else if (r2 == NULL) {
+ if ((pr = range_create(diff, r1, r2, POLDIFF_FORM_REMOVED)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ u->range = pr;
+ pr = NULL;
+ retval = 1;
+ } else {
+ if ((pr = range_create(diff, r1, r2, POLDIFF_FORM_MODIFIED)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if ((retval = range_deep_diff(diff, pr)) < 0) {
+ goto cleanup;
+ }
+ if (retval > 0) {
+ u->range = pr;
+ pr = NULL;
+ }
+ }
+ cleanup:
+ range_destroy(&pr);
+ return retval;
+}
+
+int user_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const qpol_user_t *u = item;
+ const char *name = NULL;
+ apol_vector_t *v = NULL;
+ poldiff_user_t *pu = NULL;
+ int error = 0, retval = -1;
+ if ((form == POLDIFF_FORM_ADDED &&
+ qpol_user_get_name(diff->mod_qpol, u, &name) < 0) ||
+ ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_user_get_name(diff->orig_qpol, u, &name) < 0))
+ {
+ error = errno;
+ goto cleanup;
+ }
+ if ((pu = make_diff(diff, form, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ apol_vector_destroy(&pu->added_roles);
+ if ((v = user_get_roles(diff, diff->mod_pol, u)) == NULL ||
+ (pu->added_roles = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL ||
+ user_deep_diff_default_levels(diff, NULL, u, pu) < 0 || user_deep_diff_ranges(diff, NULL, u, pu) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ } else {
+ apol_vector_destroy(&pu->removed_roles);
+ if ((v = user_get_roles(diff, diff->orig_pol, u)) == NULL ||
+ (pu->removed_roles = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL ||
+ user_deep_diff_default_levels(diff, u, NULL, pu) < 0 || user_deep_diff_ranges(diff, u, NULL, pu) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_append(diff->user_diffs->diffs, pu) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->user_diffs->num_added++;
+ } else {
+ diff->user_diffs->num_removed++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v);
+ if (retval < 0) {
+ user_free(pu);
+ errno = error;
+ }
+ return retval;
+}
+
+/**
+ * Perform a deep diff of the roles assigned to the two users.
+ *
+ * @param diff Diff structure containing the original and modified
+ * policies.
+ * @param u1 User from original policy to examine.
+ * @param u2 User from modified policy to examine.
+ * @param u Result structure where differences are to be recorded.
+ *
+ * @return Greater than zero if a diff was found, zero if none found,
+ * less than zero for errors.
+ */
+static int user_deep_diff_roles(poldiff_t * diff, const qpol_user_t * u1, const qpol_user_t * u2, poldiff_user_t * u)
+{
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ char *role1, *role2;
+ size_t i, j;
+ int retval = -1, error = 0, compval;
+
+ if ((v1 = user_get_roles(diff, diff->orig_pol, u1)) == NULL || (v2 = user_get_roles(diff, diff->mod_pol, u2)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_sort(v1, apol_str_strcmp, NULL);
+ apol_vector_sort(v2, apol_str_strcmp, NULL);
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2))
+ break;
+ role1 = (char *)apol_vector_get_element(v1, i);
+ role2 = (char *)apol_vector_get_element(v2, j);
+ compval = strcmp(role1, role2);
+ if (compval < 0) {
+ if ((role1 = strdup(role1)) == NULL || apol_vector_append(u->removed_roles, role1) < 0) {
+ error = errno;
+ free(role1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (compval > 0) {
+ if ((role2 = strdup(role2)) == NULL || apol_vector_append(u->added_roles, role2) < 0) {
+ error = errno;
+ free(role2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ if ((role1 = strdup(role1)) == NULL || apol_vector_append(u->unmodified_roles, role1) < 0) {
+ error = errno;
+ free(role1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ role1 = (char *)apol_vector_get_element(v1, i);
+ if ((role1 = strdup(role1)) == NULL || apol_vector_append(u->removed_roles, role1) < 0) {
+ error = errno;
+ free(role1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ role2 = (char *)apol_vector_get_element(v2, j);
+ if ((role2 = strdup(role2)) == NULL || apol_vector_append(u->added_roles, role2) < 0) {
+ error = errno;
+ free(role2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_get_size(u->removed_roles) > 0 || apol_vector_get_size(u->added_roles) > 0) {
+ retval = 1;
+ } else {
+ retval = 0;
+ }
+ cleanup:
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ errno = error;
+ return retval;
+}
+
+int user_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const qpol_user_t *u1 = x;
+ const qpol_user_t *u2 = y;
+ const char *name;
+ poldiff_user_t *u = NULL;
+ int retval = -1, r1 = 0, r2 = 0, r3 = 0, error = 0;
+ if (qpol_user_get_name(diff->orig_qpol, u1, &name) < 0 || (u = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((r1 = user_deep_diff_roles(diff, u1, u2, u)) < 0 || (r2 = user_deep_diff_default_levels(diff, u1, u2, u)) < 0 ||
+ (r3 = user_deep_diff_ranges(diff, u1, u2, u)) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (r1 > 0 || r2 > 0 || r3 > 0) {
+ if (apol_vector_append(diff->user_diffs->diffs, u) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->user_diffs->num_modified++;
+ } else {
+ /* no differences found */
+ user_free(u);
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ user_free(u);
+ }
+ errno = error;
+ return retval;
+}
diff --git a/libpoldiff/src/user_internal.h b/libpoldiff/src/user_internal.h
new file mode 100644
index 0000000..f94d46b
--- /dev/null
+++ b/libpoldiff/src/user_internal.h
@@ -0,0 +1,121 @@
+/**
+ * @file
+ * Protected interface for user differences.
+ *
+ * @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 POLDIFF_USER_INTERNAL_H
+#define POLDIFF_USER_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_user_summary poldiff_user_summary_t;
+
+/**
+ * Allocate and return a new poldiff_user_summary_t object.
+ *
+ * @return A new user summary. The caller must call user_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_user_summary_t *user_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_user_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param us Reference to a user summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void user_destroy(poldiff_user_summary_t ** us);
+
+/**
+ * Reset the state of all user differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int user_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all users from the given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all users. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *user_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_user_t objects, determining if they have the same
+ * name or not.
+ *
+ * @param x The user from the original policy.
+ * @param y The user from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if user x is respectively less than, equal
+ * to, or greater than user y.
+ */
+ int user_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a user.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int user_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two users for which the compare
+ * callback returns 0. If a difference is found then allocate,
+ * initialize, and insert a new semantic difference entry for that
+ * user.
+ *
+ * @param diff The policy difference structure associated with both
+ * users and to which to add an entry if needed.
+ * @param x The user from the original policy.
+ * @param y The user from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int user_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_USER_INTERNAL_H */
diff --git a/libpoldiff/src/util.c b/libpoldiff/src/util.c
new file mode 100644
index 0000000..059dadf
--- /dev/null
+++ b/libpoldiff/src/util.c
@@ -0,0 +1,32 @@
+/**
+ * @file
+ *
+ * Implementation of utility functions for libpoldiff.
+ *
+ * @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 <poldiff/util.h>
+
+const char *libpoldiff_get_version(void)
+{
+ return LIBPOLDIFF_VERSION_STRING;
+}
diff --git a/libpoldiff/src/writing-diffs-HOWTO b/libpoldiff/src/writing-diffs-HOWTO
new file mode 100644
index 0000000..4980492
--- /dev/null
+++ b/libpoldiff/src/writing-diffs-HOWTO
@@ -0,0 +1,351 @@
+Writing New Diff Modules for libpoldiff HOWTO
+August 1, 2007
+
+
+0. Introduction
+
+libpoldiff is a library to be used in conjunction with libapol to find
+"semantic" differences between policies. For the purposes of this
+HOWTO, the term "semantic" refers to how the SELinux kernel would
+enforce accesses. If two policies could ever be enforced differently
+then they are defined to be semantically different.
+
+libpoldiff operates by breaking a policy into various 'policy items'.
+Examples of items are users, object classes, and type enforcement
+rules. These items correspond to flags passed into the sediff
+program. So as to be extensible, libpoldiff was designed to allow one
+to add new diff modules (and hence additional flags to sediff). These
+modules should operate independent of each other and without regard to
+ordering of modules.
+
+
+1. Library Overview
+
+libpoldiff implements what we term "the generic diffing algorithm",
+akin to a merge sort. The algorithm takes two ordered lists of items
+and successively picks the first item from the lists. If the two
+match, according to the items' comparison function, then they are
+deemed the same. Otherwise the same ordering used to generate the
+lists may also be used to determine if one was added or removed from
+the policy.
+
+As an example, consider a diff of users for two hypothetical SELinux
+policies, "orig.policy" and "updated.policy". Within orig.policy are
+users adam_u, charlie_u, and dave_u. updated.policy has the users adam_u,
+bob_u, and charlie_u. The first step of the generic diffing algorithm
+is to get the ordered list of items. Let the ordering algorithm be
+alphabetical order; thus the lists would be:
+
+ orig.policy -> {adam_u, charlie_u, dave_u}
+ updated.policy -> {adam_u, bob_u, charlie_u}
+
+The algorithm picks the first item from each list and compares them.
+As that "adam_u" is the same as "adam_u", the algorithm accepts them
+as the same and continues. The next two items are "charlie_u" and
+"bob_u". The ordering functions finds "bob_u" to be "earlier" than
+"charlie_u" (because it appears earlier alphabetically), "bob_u" is
+marked as item added to updated.policy. The algorithm keeps
+"charlie_u" from the first policy but advances the pointer for the
+second list. These "charlie_u" are the same. The remaining type,
+"dave_u", has no complement in the second list and is thus marked as
+removed.
+
+Of course, finding differences for users is not as simple as comparing
+their names. In addition one must also examine the roles assigned to
+them as well. Comparing names was a "shallow diff"; checking roles is
+a "deep diff". The deep diff must look at all aspects of the two
+items to determine if they are the same, modified,
+modified-by-adding-a-type, or modified-by-removing-a-type. (At this
+time the users' allowed MLS ranges and default range would also be
+checked.)
+
+To complicate things, consider the aspect of type remapping. Type
+names may be changed between the policies; they could also be joined
+into a new type and conversely split. Thus one must be careful how
+the ordered lists of types are generated. The functions
+type_map_lookup() and type_map_lookup_reverse() will prove essential.
+
+
+2. Reporting Differences
+
+If a difference was found, either via a shallow diff or a deep diff,
+then an "item diff" struct must be created. If it the difference was
+'added' or 'removed' then libpoldiff's poldiff_do_item_diff() will
+call the item diff creation function. If instead the difference was
+found within the deep diff comparison function then that function is
+responsible for creating the item diff struct. The item diff struct
+is used by each item's to_string() function to create a human-readable
+report.
+
+
+3. Complete Walkthrough
+
+The following walkthrough describes the process for writing a diff for
+items between the original and modified policies. The shallow
+diff is to see if a type in the original policy exists in the modified
+policy, with respect to the type map. The deep diff determines if the
+types have the same set of attributes.
+
+
+3a. Public Header
+
+Create a new file, libpoldiff/include/poldiff/type_diff.h, to declare
+publicly accessible functions. At least three functions must exist
+here:
+
+ extern void poldiff_type_get_stats(poldiff_t *diff, size_t stats[5]);
+ extern apol_vector_t *poldiff_get_type_vector(poldiff_t *diff);
+ extern poldiff_form_e *poldiff_type_get_form(const void *type);
+ extern char *poldiff_type_to_string(poldiff_t *diff, const void *type);
+
+(The reason for using a void * in poldiff_type_to_string() will become
+apparant in section 3d.)
+
+Also in this file, declare an opaque object to hold a single type
+difference:
+
+ typedef struct poldiff_type poldiff_type_t;
+
+Once a user gets a vector of poldiff_type_t objects, via
+poldiff_get_type_vector(), he may want to do a number of things with
+it. He could print it via poldiff_type_to_string() or get its form;
+or just get the type's name, list of added attributes, or list of
+removed attributes. Therefore add these three functions to
+type_diff.h:
+
+ extern const char *poldiff_type_get_name(poldiff_type_t *type);
+ extern apol_vector_t *poldiff_type_get_added_attribs(poldiff_type_t *type);
+ extern apol_vector_t *poldiff_type_get_removed_attribs(poldiff_type_t *type);
+
+As a convenience to developers, one should only need to #include the
+public poldiff.h to pick up all diff modules. Modify
+libpoldiff/include/poldiff/poldiff.h in the vicinity of line 74:
+
+ #include <poldiff/type_diff.h>
+
+Finally, modify libpoldiff/include/poldiff/Makefile.am by adding an
+entry for type_diff.h.
+
+3b. Protected Header
+
+There will be functions accessible only between library files (i.e.,
+protected functions). To distinguish public functions from those that
+are protected, do not prefix these with 'poldiff_'. Create a new
+file, libpoldiff/src/type_internal.h, that declares protected
+routines. libpoldiff requires these four functions to exist:
+
+ apol_vector_t *type_get_items(poldiff_t *diff, apol_policy_t *policy);
+ int type_new_diff(poldiff_t *diff, poldiff_form_e form, const void *item);
+ int type_comp(const void *x, const void *y, poldiff_t *diff);
+ int type_deep_diff(poldiff_t *diff, const void *x, const void *y);
+
+Associated with the computed list of poldiff_type_t objects is a
+summary structure. Check that libpoldiff/src/poldiff_internal.h has
+declare a 'struct poldiff_type_summary', then add the following line
+to the protected header:
+
+ typedef struct poldiff_type_summary poldiff_type_summary_t;
+
+As with all other poldiff objects, you will need a constructor and a
+destructor:
+
+ poldiff_type_summary_t *type_create(void);
+ void type_destroy(poldiff_type_summary_t **ts);
+
+As a convenience to developers, one should only need to #include the
+protected poldiff_internal.h to pick up all diff modules. Modify
+libpoldiff/src/poldiff_internal.h in the vicinity of line 50:
+
+ #include "type_internal.h"
+
+Finally, modify libpoldiff/src/Makefile.am by adding an entry for
+type_internal.h.
+
+3c. Implementing Functions
+
+Create a new file, libpoldiff/src/type_diff.c, to implement all
+public, protected, and any necessary private functions. First declare
+the contents of the structures:
+
+ struct poldiff_type_summary {
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs; /* vector of poldiff_type_t */
+ };
+ struct poldiff_type {
+ char *name;
+ poldiff_form_e form;
+ apol_vector_t *added_attribs; /* vector of char* */
+ apol_vector_t *removed_attribs; /* vector of char* */
+ };
+
+The public functions are easy to write.
+
+ * poldiff_type_get_stats() fetches the fields
+ diff->type_diffs->num_added, diff->type_diffs->num_removed, and
+ diff->type_diffs->num_modified.
+
+ * poldiff_type_get_form() fetches an individual result's form. Note
+ that you must first cast the second parameter from void* to a
+ poldiff_type_t*, because this function operates upon items
+ returned by poldiff_get_type_vector().
+
+ * poldiff_type_to_string() returns an allocated string akin to
+ poldiff_user_to_string(). Note that you must first cast the
+ second parameter from void* to a poldiff_type_t*, because this
+ function operates upon items returned by
+ poldiff_get_type_vector().
+
+ * poldiff_get_type_vector() returns diff->type_diffs->diffs.
+
+The rest of the public functions are accessors into a poldiff_type_t
+object.
+
+The protected functions are more difficult.
+
+ * type_create() and type_destroy() affect a poldiff_type_summary_t
+ object.
+
+ * The other protected functions are described in section 3e.
+
+Finally, modify libpoldiff/src/Makefile.am by adding an entry for
+type_diff.c.
+
+3d. Adding Hooks to Diff Module
+
+The main library now needs to create and destroy the
+poldiff_type_summary_t object and to actually diff types. Make these
+changes to libpoldiff/src/poldiff.c:
+
+ * Create a poldiff_type_summary_t object by calling type_create()
+ within poldiff_create().
+
+ * Destroy the summary by calling type_destroy() within
+ poldiff_destroy().
+
+ * To enforce that all diff modules have the requisite public and
+ protected functions, one must fulfill the requirements as given by
+ a poldiff_item_record, as defined on line 41. The first four
+ callbacks are satisfied by the first four public functions, the
+ remainder are met by protected functions. Thus add a new record
+ to the item_records[] array like so:
+
+ /* ... */
+ {
+ "type",
+ POLDIFF_DIFF_TYPES,
+ poldiff_type_get_stats,
+ poldiff_get_type_vector,
+ poldiff_type_get_form,
+ poldiff_type_to_string,
+ type_reset,
+ type_get_items,
+ type_comp,
+ type_new_diff,
+ type_deep_diff
+ },
+ /* ... */
+
+Finally, for the public functions to be accessible through
+libpoldiff.so, add this line to libpoldiff/src/libpoldiff.map under
+the 'global' category:
+
+ poldiff_type_*;
+
+3e. General Idea for Diffing Types
+
+Rather than comparing qpol_type_t pointers from one policy to another,
+it is more convenient to convert those pointers to "pseudo-type
+values", which are represented as uint32_ts. These new values handle
+the type mappings between policies. Whenever a difference is found,
+convert those pseudo-type values back to the component qpol_type_t
+pointers. With type splits and type joins, a single pseudo-type value
+may map to multiple qpol_type_t pointers.
+
+First look at type_get_items(). Its job is to return a sorted list of
+unique items. Write it like this:
+
+ apol_vector_t *type_get_items(poldiff_t *diff, apol_policy_t *policy)
+ {
+ get an iterator of types from the policy
+ allocate a new vector v
+ for each item in the iterator,
+ convert qpol_type_t* to uint32_t via type_map_lookup()
+ append that uint32_t to v
+ sort and unquify v
+ return v
+ }
+
+type_map_lookup() needs a third parameter that says from which policy
+the type originated. Use these lines to calculate the parameter:
+
+ if (policy == diff->orig_pol)
+ which_pol = POLDIFF_POLICY_ORIG;
+ else
+ which_pol = POLDIFF_POLICY_MOD;
+
+Now that you have a vector of pseudo-type values, all further
+functions will need to be in terms of these values. libpoldiff will
+pass elements from the type_get_items() vector into your protected
+functions. In this walkthrough you will thus need to cast all values
+from void* to uint32_t because type_get_items() returned a vector of
+uint32_ts. For example:
+
+ int type_comp(const void *x, const void *y,
+ poldiff_t *diff __attribute__((unused)))
+ {
+ uint32_t t1 = (uint32_t) x;
+ uint32_t t2 = (uint32_t) y;
+ return t1 - t2;
+ }
+
+ int type_deep_diff(poldiff_t *diff, const void *x, const void *y)
+ {
+ uint32_t t1 = (uint32_t) x;
+ uint32_t t2 = (uint32_t) y;
+ apol_vector_t *v1 = type_map_lookup_reverse(diff, t1,
+ POLDIFF_POLICY_ORIG);
+ apol_vector_t *v2 = type_map_lookup_reverse(diff, t2,
+ POLDIFF_POLICY_ORIG);
+ apol_vector_t *added_attribs, *removed_attribs;
+ let vector a1 = union of all attributes for all types in vector v1
+ let vector a2 = union of all attributes for all types in vector v2
+ sort and uniquify a1
+ sort and uniquify a2
+ for all attributes in a1 not in a2,
+ append to removed_attribs those attributes
+ for all attributes in a2 not in a1,
+ append to added_attribs those attributes
+ if removed_attribs is not empty or added_attribs is not empty,
+ then foreach type in v1,
+ create a new poldiff_type_t
+ set the poldiff_type_t's name to the type's name
+ set the form to POLDIFF_FORM_MODIFIED
+ clone added_attribs
+ clone removed_attribs
+ append the poldiff_type_t to diff->type_diffs->diffs
+ if no poldiff_type_t was created
+ return 0
+ else
+ return non-zero
+ }
+
+ int type_new_diff(poldiff_t *diff, poldiff_form_e form, const void *item)
+ {
+ uint32_t t = (uint32_t) item;
+ apol_vector_t *v;
+ if (form == POLDIFF_FORM_REMOVED)
+ v = type_map_lookup(diff, t, POLDIFF_POLICY_ORIG);
+ else
+ v = type_map_lookup(diff, t, POLDIFF_POLICY_MOD);
+ foreach type in v,
+ create a new poldiff_type_t
+ set the poldiff_type_t's name to the type's name
+ set the form to form
+ append the poldiff_type_t to diff->type_diffs->diffs
+ }
+
+One implementation of creating temporary vectors similar to
+added_attribs and removed_attribs may be found at
+libpoldiff/src/role_diff.c:role_deep_diff().
diff --git a/libpoldiff/swig/Makefile.am b/libpoldiff/swig/Makefile.am
new file mode 100644
index 0000000..3412858
--- /dev/null
+++ b/libpoldiff/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 = poldiff.i
diff --git a/libpoldiff/swig/java/MANIFEST.MF.in b/libpoldiff/swig/java/MANIFEST.MF.in
new file mode 100644
index 0000000..dd680b0
--- /dev/null
+++ b/libpoldiff/swig/java/MANIFEST.MF.in
@@ -0,0 +1,14 @@
+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.poldiff"
+Implementation-Version: "@libpoldiff_version@"
+Implementation-Vendor: "Tresys Technology"
+Extension-List: qpol apol
+qpol-Extension-Name: com.tresys.setools.qpol
+qpol-Implementation-Version: @libqpol_version@
+apol-Extension-Name: com.tresys.setools.apol
+apol-Implementation-Version: @libapol_version@
diff --git a/libpoldiff/swig/java/Makefile.am b/libpoldiff/swig/java/Makefile.am
new file mode 100644
index 0000000..6be72d3
--- /dev/null
+++ b/libpoldiff/swig/java/Makefile.am
@@ -0,0 +1,93 @@
+wrappedso_DATA = libjpoldiff.so.@libpoldiff_version@
+wrappedso_SONAME = @libpoldiff_jswig_soname@
+short_name = libjpoldiff.so
+wrappedsodir = $(libdir)
+
+package_name = com.tresys.setools.poldiff
+package_dir = $(dir $(subst .,/,$(package_name)))poldiff
+
+wrappedjar_DATA = poldiff.jar
+wrappedjardir = $(setoolsdir)
+
+dist_noinst_DATA = $(srcdir)/../poldiff.i
+BUILT_SOURCES = poldiff_wrap.c \
+ poldiff_attrib_t.java \
+ poldiff_avrule_t.java \
+ poldiff_bool_t.java \
+ poldiff_cat_t.java \
+ poldiff_class_t.java \
+ poldiff_common_t.java \
+ poldiffConstants.java \
+ poldiff_form_e.java \
+ poldiff.java \
+ poldiffJNI.java \
+ poldiff_level_t.java \
+ poldiff_range_t.java \
+ poldiff_range_trans_t.java \
+ poldiff_role_allow_t.java \
+ poldiff_role_t.java \
+ poldiff_role_trans_t.java \
+ poldiff_stats_t.java \
+ poldiff_terule_t.java \
+ poldiff_t.java \
+ poldiff_type_remap_entry_t.java \
+ poldiff_type_t.java \
+ poldiff_user_t.java \
+ SWIGTYPE_p_void.java
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libpoldiff/include
+AM_JFLAGS = @DEBUGJFLAGS@ @WARNJFLAGS@ \
+ -classpath $(top_builddir)/libqpol/swig/java/qpol.jar:$(top_builddir)/libapol/swig/java/apol.jar
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \
+ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @POLDIFF_LIB_FLAG@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libpoldiff/src/libpoldiff.so
+
+$(firstword $(BUILT_SOURCES)): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) $(SWIG_JAVA_OPT) -package $(package_name) -o $@ \
+ -I$(top_srcdir)/libpoldiff/include -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/include \
+ -I$(top_srcdir)/libqpol/swig -I$(top_srcdir)/libapol/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) -DSWIGJAVA=1 $(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.poldiff.
+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/libpoldiff/swig/poldiff.i b/libpoldiff/swig/poldiff.i
new file mode 100644
index 0000000..29767a8
--- /dev/null
+++ b/libpoldiff/swig/poldiff.i
@@ -0,0 +1,1335 @@
+/**
+ * @file
+ * SWIG declarations for libpoldiff.
+ *
+ * @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 poldiff
+
+%{
+#include <poldiff/attrib_diff.h>
+#include <poldiff/avrule_diff.h>
+#include <poldiff/bool_diff.h>
+#include <poldiff/cat_diff.h>
+#include <poldiff/class_diff.h>
+#include <poldiff/level_diff.h>
+#include <poldiff/poldiff.h>
+#include <poldiff/range_diff.h>
+#include <poldiff/range_trans_diff.h>
+#include <poldiff/rbac_diff.h>
+#include <poldiff/role_diff.h>
+#include <poldiff/terule_diff.h>
+#include <poldiff/type_diff.h>
+#include <poldiff/type_map.h>
+#include <poldiff/user_diff.h>
+#include <poldiff/util.h>
+
+/* Provide hooks so that language-specific modules can define the
+ * callback function, used by the handler in poldiff_create().
+ */
+SWIGEXPORT poldiff_handle_fn_t poldiff_swig_message_callback = NULL;
+SWIGEXPORT void * poldiff_swig_message_callback_arg = NULL;
+
+%}
+
+#ifdef SWIGJAVA
+%javaconst(1);
+/* get the java environment so we can throw exceptions */
+%{
+ static JNIEnv *poldiff_global_jenv;
+ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+ (*vm)->AttachCurrentThread(vm, (void **)&poldiff_global_jenv, NULL);
+ return JNI_VERSION_1_2;
+ }
+%}
+#endif
+
+%include exception.i
+%include stdint.i
+%import apol.i
+
+%{
+#undef BEGIN_EXCEPTION
+#undef END_EXCEPTION
+%}
+
+#ifdef SWIGJAVA
+
+%exception {
+ poldiff_global_jenv = jenv;
+ $action
+}
+
+%{
+#define BEGIN_EXCEPTION JNIEnv *local_jenv = poldiff_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.*;
+import com.tresys.setools.apol.*;
+%}
+%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 and apol */
+%pragma(java) jniclassimports=%{
+import com.tresys.setools.qpol.*;
+import com.tresys.setools.apol.*;
+%}
+%pragma(java) jniclasscode=%{
+ static {
+ try
+ {
+ libpoldiff_get_version ();
+ }
+ catch (UnsatisfiedLinkError ule)
+ {
+ System.loadLibrary("jpoldiff");
+ }
+ }
+%}
+%pragma(java) moduleimports=%{
+import com.tresys.setools.qpol.*;
+import com.tresys.setools.apol.*;
+%}
+#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;}
+#define SWIG_exception_typemap(code, msg) {SWIG_JavaException(jenv, code, msg);}
+%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
+ * Poldiff_Init(), but the output file will be called libtpoldiff.so instead
+ * of libpoldiff.so. Therefore add an alias from Tpoldiff_Init() to the
+ * real Poldiff_Init().
+ */
+SWIGEXPORT int Tpoldiff_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
+
+%inline %{
+ typedef struct apol_string_vector apol_string_vector_t;
+%}
+
+const char *libpoldiff_get_version (void);
+
+/* diff element flags */
+#define POLDIFF_DIFF_CLASSES 0x00000001
+#define POLDIFF_DIFF_COMMONS 0x00000002
+#define POLDIFF_DIFF_TYPES 0x00000004
+#define POLDIFF_DIFF_ATTRIBS 0x00000008
+#define POLDIFF_DIFF_ROLES 0x00000010
+#define POLDIFF_DIFF_USERS 0x00000020
+#define POLDIFF_DIFF_BOOLS 0x00000040
+#define POLDIFF_DIFF_LEVELS 0x00000080
+#define POLDIFF_DIFF_CATS 0x00000100
+#define POLDIFF_DIFF_AVRULES 0x00000200
+#define POLDIFF_DIFF_TERULES 0x00000400
+#define POLDIFF_DIFF_ROLE_ALLOWS 0x00000800
+#define POLDIFF_DIFF_ROLE_TRANS 0x00001000
+#define POLDIFF_DIFF_RANGE_TRANS 0x00002000
+#define POLDIFF_DIFF_SYMBOLS (POLDIFF_DIFF_CLASSES|POLDIFF_DIFF_COMMONS|POLDIFF_DIFF_TYPES|POLDIFF_DIFF_ATTRIBS|POLDIFF_DIFF_ROLES|POLDIFF_DIFF_USERS|POLDIFF_DIFF_BOOLS)
+#define POLDIFF_DIFF_RULES (POLDIFF_DIFF_AVRULES|POLDIFF_DIFF_TERULES|POLDIFF_DIFF_ROLE_ALLOWS|POLDIFF_DIFF_ROLE_TRANS)
+#define POLDIFF_DIFF_RBAC (POLDIFF_DIFF_ROLES|POLDIFF_DIFF_ROLE_ALLOWS|POLDIFF_DIFF_ROLE_TRANS)
+#define POLDIFF_DIFF_MLS (POLDIFF_DIFF_LEVELS|POLDIFF_DIFF_CATS|POLDIFF_DIFF_RANGE_TRANS)
+/* NOTE: while defined OCONS are not currently supported */
+#define POLDIFF_DIFF_OCONS 0
+#define POLDIFF_DIFF_REMAPPED (POLDIFF_DIFF_TYPES|POLDIFF_DIFF_ATTRIBS|POLDIFF_DIFF_AVRULES|POLDIFF_DIFF_TERULES|POLDIFF_DIFF_ROLES|POLDIFF_DIFF_ROLE_TRANS|POLDIFF_DIFF_RANGE_TRANS|POLDIFF_DIFF_OCONS)
+#define POLDIFF_DIFF_ALL (POLDIFF_DIFF_SYMBOLS|POLDIFF_DIFF_RULES|POLDIFF_DIFF_MLS|POLDIFF_DIFF_OCONS)
+
+%typemap(check) uint32_t flags {
+ if ($1 & ~(POLDIFF_DIFF_ALL)) {
+#ifdef SWIGJAVA
+ SWIG_exception_typemap(SWIG_ValueError, "Invalid diff flag specified");
+#else
+ SWIG_exception(SWIG_ValueError, "Invalid diff flag specified");
+#endif
+ }
+}
+
+%inline %{
+/* if java, pass the new exception macro to C not just SWIG */
+#ifdef SWIGJAVA
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {SWIG_JavaException(jenv, code, msg); goto fail;}
+#endif
+%}
+
+/* poldiff form */
+typedef enum poldiff_form
+{
+ POLDIFF_FORM_NONE,
+ POLDIFF_FORM_ADDED,
+ POLDIFF_FORM_REMOVED,
+ POLDIFF_FORM_MODIFIED,
+ POLDIFF_FORM_ADD_TYPE,
+ POLDIFF_FORM_REMOVE_TYPE
+} poldiff_form_e;
+
+/* for handling the get_stats function */
+%{
+ typedef struct poldiff_stats {
+ size_t stats[5];
+ } poldiff_stats_t;
+ poldiff_stats_t *poldiff_stats_create() {
+ return calloc(1, sizeof(poldiff_stats_t));
+ }
+ void poldiff_stats_destroy(poldiff_stats_t **x) {
+ if (!x || !(*x))
+ return;
+ free(*x);
+ *x = NULL;
+ }
+%}
+typedef struct poldiff_stats {} poldiff_stats_t;
+%extend poldiff_stats_t {
+ poldiff_stats_t() {
+ poldiff_stats_t *s;
+ BEGIN_EXCEPTION
+ s = poldiff_stats_create();
+ if (!s) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return s;
+ };
+ ~poldiff_stats_t() {
+ poldiff_stats_destroy(&self);
+ };
+ size_t get_stat(poldiff_form_e form) {
+ BEGIN_EXCEPTION
+ switch(form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ return self->stats[0];
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ return self->stats[1];
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ return self->stats[2];
+ }
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ return self->stats[3];
+ }
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ return self->stats[4];
+ }
+ case POLDIFF_FORM_NONE:
+ default:
+ {
+ SWIG_exception(SWIG_ValueError, "Invalid poldiff form");
+ }
+ }
+ END_EXCEPTION
+ fail:
+ return 0;
+ };
+};
+
+/* for handling vector of line numbers stored as unsigned long but returned as void* */
+%{
+ unsigned long to_ulong(void *x) {
+ return (unsigned long)x;
+ }
+%}
+unsigned long to_ulong(void *x);
+
+/* poldiff object */
+#ifdef SWIGPYTHON
+/* the following type maps handle the poldiff object taking ownership of the policies */
+%typemap(in) apol_policy_t *op {
+ void *x = NULL;
+ Py_IncRef($input);
+ SWIG_ConvertPtr($input, &x,SWIGTYPE_p_apol_policy, 0 | 0 );
+ $1 = (apol_policy_t*)x;
+}
+%typemap(in) apol_policy_t *mp {
+ void *x = NULL;
+ Py_IncRef($input);
+ SWIG_ConvertPtr($input, &x,SWIGTYPE_p_apol_policy, 0 | 0 );
+ $1 = (apol_policy_t*)x;
+}
+#endif
+typedef struct poldiff {} poldiff_t;
+%extend poldiff_t {
+ poldiff_t(apol_policy_t *op, apol_policy_t *mp) {
+ poldiff_t *p;
+ BEGIN_EXCEPTION
+ p = poldiff_create(op, mp, poldiff_swig_message_callback, poldiff_swig_message_callback_arg);
+ if (!p) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ return p;
+ fail:
+ return NULL;
+ };
+ ~poldiff_t() {
+ poldiff_destroy(&self);
+ };
+ void run(uint32_t flags) {
+ BEGIN_EXCEPTION
+ if (poldiff_run(self, flags)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run diff");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ int is_run(uint32_t flags) {
+ return poldiff_is_run(self, flags);
+ };
+ %newobject get_stats(uint32_t);
+ poldiff_stats_t *get_stats(uint32_t flags) {
+ poldiff_stats_t *s = NULL;
+ BEGIN_EXCEPTION
+ s = poldiff_stats_create();
+ if (!s) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ if (poldiff_get_stats(self, flags, s->stats)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get stats");
+ }
+ END_EXCEPTION
+ return s;
+ fail:
+ poldiff_stats_destroy(&s);
+ return NULL;
+ };
+ void enable_line_numbers() {
+ BEGIN_EXCEPTION
+ if (poldiff_enable_line_numbers(self)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not enable line numbers");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const apol_vector_t *get_attrib_vector() {
+ return poldiff_get_attrib_vector(self);
+ };
+ const apol_vector_t *get_avrule_vector_allow() {
+ return poldiff_get_avrule_vector_allow(self);
+ };
+ const apol_vector_t *get_avrule_vector_auditallow() {
+ return poldiff_get_avrule_vector_auditallow(self);
+ };
+ const apol_vector_t *get_avrule_vector_dontaudit() {
+ return poldiff_get_avrule_vector_dontaudit(self);
+ };
+ const apol_vector_t *get_avrule_vector_neverallow() {
+ return poldiff_get_avrule_vector_neverallow(self);
+ };
+ const apol_vector_t *get_bool_vector() {
+ return poldiff_get_bool_vector(self);
+ };
+ const apol_vector_t *get_cat_vector() {
+ return poldiff_get_cat_vector(self);
+ };
+ const apol_vector_t *get_class_vector() {
+ return poldiff_get_class_vector(self);
+ };
+ const apol_vector_t *get_common_vector() {
+ return poldiff_get_common_vector(self);
+ };
+ const apol_vector_t *get_level_vector() {
+ return poldiff_get_level_vector(self);
+ };
+ const apol_vector_t *get_range_trans_vector() {
+ return poldiff_get_range_trans_vector(self);
+ };
+ const apol_vector_t *get_role_allow_vector() {
+ return poldiff_get_role_allow_vector(self);
+ };
+ const apol_vector_t *get_role_trans_vector() {
+ return poldiff_get_role_trans_vector(self);
+ };
+ const apol_vector_t *get_role_vector() {
+ return poldiff_get_role_vector(self);
+ };
+ const apol_vector_t *get_terule_vector_change() {
+ return poldiff_get_terule_vector_change(self);
+ };
+ const apol_vector_t *get_terule_vector_member() {
+ return poldiff_get_terule_vector_member(self);
+ };
+ const apol_vector_t *get_terule_vector_trans() {
+ return poldiff_get_terule_vector_trans(self);
+ };
+ const apol_vector_t *get_type_vector() {
+ return poldiff_get_type_vector(self);
+ };
+ const apol_vector_t *get_user_vector() {
+ return poldiff_get_user_vector(self);
+ };
+ const apol_vector_t *get_type_remap_entries() {
+ return poldiff_type_remap_get_entries(self);
+ };
+ void type_remap_create(apol_string_vector_t *orig_types, apol_string_vector_t *mod_types) {
+ BEGIN_EXCEPTION
+ if (poldiff_type_remap_create(self, (apol_vector_t*)orig_types, (apol_vector_t*)mod_types)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not remap types");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void type_remap_remove(poldiff_type_remap_entry_t *ent) {
+ poldiff_type_remap_entry_remove(self, ent);
+ };
+};
+
+/* attribute diff */
+typedef struct poldiff_attrib {} poldiff_attrib_t;
+%extend poldiff_attrib_t {
+ poldiff_attrib_t () {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_attrib_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_attrib_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_attrib_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_attrib_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_attrib_get_form(self);
+ };
+ const apol_string_vector_t *get_added_types() {
+ return (apol_string_vector_t*)poldiff_attrib_get_added_types(self);
+ };
+ const apol_string_vector_t *get_removed_types() {
+ return (apol_string_vector_t*)poldiff_attrib_get_removed_types(self);
+ };
+};
+%inline %{
+ poldiff_attrib_t *poldiff_attrib_from_void(void *x) {
+ return (poldiff_attrib_t*)x;
+ };
+%}
+
+/* av rule diff */
+typedef struct poldiff_avrule {} poldiff_avrule_t;
+%extend poldiff_avrule_t {
+ poldiff_avrule_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_avrule_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_avrule_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_avrule_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ poldiff_form_e get_form() {
+ return poldiff_avrule_get_form(self);
+ };
+ uint32_t get_rule_type() {
+ return poldiff_avrule_get_rule_type(self);
+ };
+ const char *get_source_type() {
+ return poldiff_avrule_get_source_type(self);
+ };
+ const char *get_target_type() {
+ return poldiff_avrule_get_target_type(self);
+ };
+ const char *get_object_class() {
+ return poldiff_avrule_get_object_class(self);
+ };
+ const qpol_cond_t *get_cond(poldiff_t *p) {
+ const qpol_cond_t *cond;
+ uint32_t which_list;
+ const apol_policy_t *which_pol;
+ poldiff_avrule_get_cond(p, self, &cond, &which_list, &which_pol);
+ return cond;
+ };
+ uint32_t get_cond_list(poldiff_t *p) {
+ const qpol_cond_t *cond;
+ uint32_t which_list;
+ const apol_policy_t *which_pol;
+ poldiff_avrule_get_cond(p, self, &cond, &which_list, &which_pol);
+ return which_list;
+ };
+ const apol_policy_t *get_cond_policy(poldiff_t *p) {
+ const qpol_cond_t *cond;
+ uint32_t which_list;
+ const apol_policy_t *which_pol;
+ poldiff_avrule_get_cond(p, self, &cond, &which_list, &which_pol);
+ return which_pol;
+ };
+ const apol_string_vector_t *get_unmodified_perms() {
+ return (apol_string_vector_t*)poldiff_avrule_get_unmodified_perms(self);
+ };
+ const apol_string_vector_t *get_added_perms() {
+ return (apol_string_vector_t*)poldiff_avrule_get_added_perms(self);
+ };
+ const apol_string_vector_t *get_removed_perms() {
+ return (apol_string_vector_t*)poldiff_avrule_get_removed_perms(self);
+ };
+ const apol_vector_t *get_orig_line_numbers() {
+ return poldiff_avrule_get_orig_line_numbers(self);
+ };
+ %newobject get_orig_line_numbers_for_perm(poldiff_t*, char*);
+ apol_vector_t *get_orig_line_numbers_for_perm(poldiff_t *p, char *perm) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ v = poldiff_avrule_get_orig_line_numbers_for_perm(p, self, perm);
+ if (!v) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ const apol_vector_t *get_mod_line_numbers() {
+ return poldiff_avrule_get_mod_line_numbers(self);
+ };
+ %newobject get_mod_line_numbers_for_perm(poldiff_t*, char*);
+ apol_vector_t *get_mod_line_numbers_for_perm(poldiff_t *p, char *perm) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ v = poldiff_avrule_get_mod_line_numbers_for_perm(p, self, perm);
+ if (!v) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+};
+%inline %{
+ poldiff_avrule_t *poldiff_avrule_from_void(void *x) {
+ return (poldiff_avrule_t*)x;
+ };
+%}
+
+/* boolean diff */
+typedef struct poldiff_bool {} poldiff_bool_t;
+%extend poldiff_bool_t {
+ poldiff_bool_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_bool_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_bool_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_bool_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_bool_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_bool_get_form(self);
+ };
+};
+%inline %{
+ poldiff_bool_t *poldiff_bool_from_void(void *x) {
+ return (poldiff_bool_t*)x;
+ };
+%}
+
+/* category diff */
+typedef struct poldiff_cat {} poldiff_cat_t;
+%extend poldiff_cat_t {
+ poldiff_cat_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_cat_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_cat_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_cat_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_cat_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_cat_get_form(self);
+ };
+};
+%inline %{
+ poldiff_cat_t *poldiff_cat_from_void(void *x) {
+ return (poldiff_cat_t*)x;
+ };
+%}
+
+/* class diff */
+typedef struct poldiff_class {} poldiff_class_t;
+%extend poldiff_class_t {
+ poldiff_class_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_class_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_class_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_class_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_class_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_class_get_form(self);
+ };
+ const apol_string_vector_t *get_added_perms() {
+ return (apol_string_vector_t*)poldiff_class_get_added_perms(self);
+ };
+ const apol_string_vector_t *get_removed_perms() {
+ return (apol_string_vector_t*)poldiff_class_get_removed_perms(self);
+ };
+};
+%inline %{
+ poldiff_class_t *poldiff_class_from_void(void *x) {
+ return (poldiff_class_t*)x;
+ };
+%}
+
+/* common diff */
+typedef struct poldiff_common {} poldiff_common_t;
+%extend poldiff_common_t {
+ poldiff_common_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_common_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_common_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_common_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_common_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_common_get_form(self);
+ };
+ const apol_string_vector_t *get_added_perms() {
+ return (apol_string_vector_t*)poldiff_common_get_added_perms(self);
+ };
+ const apol_string_vector_t *get_removed_perms() {
+ return (apol_string_vector_t*)poldiff_common_get_removed_perms(self);
+ };
+};
+%inline %{
+ poldiff_common_t *poldiff_common_from_void(void *x) {
+ return (poldiff_common_t*)x;
+ };
+%}
+
+/* level diff */
+typedef struct poldiff_level {} poldiff_level_t;
+%extend poldiff_level_t {
+ poldiff_level_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_level_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_level_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_level_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ %newobject to_string_brief(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_level_to_string_brief(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_level_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_level_get_form(self);
+ };
+ const apol_string_vector_t *get_unmodified_cats() {
+ return (apol_string_vector_t*)poldiff_level_get_unmodified_cats(self);
+ };
+ const apol_string_vector_t *get_added_cats() {
+ return (apol_string_vector_t*)poldiff_level_get_added_cats(self);
+ };
+ const apol_string_vector_t *get_removed_cats() {
+ return (apol_string_vector_t*)poldiff_level_get_removed_cats(self);
+ };
+};
+%inline %{
+ poldiff_level_t *poldiff_level_from_void(void *x) {
+ return (poldiff_level_t*)x;
+ };
+%}
+
+/* range diff */
+typedef struct poldiff_range {} poldiff_range_t;
+%extend poldiff_range_t {
+ poldiff_range_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_range_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_range_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string_brief(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_range_to_string_brief(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const apol_vector_t *get_levels() {
+ return poldiff_range_get_levels(self);
+ };
+ const apol_mls_range_t *get_original_range() {
+ return poldiff_range_get_original_range(self);
+ };
+ const apol_mls_range_t *get_modified_range() {
+ return poldiff_range_get_modified_range(self);
+ };
+ const apol_string_vector_t *get_min_added_cats() {
+ return (apol_string_vector_t*)poldiff_range_get_min_added_cats(self);
+ };
+ const apol_string_vector_t *get_min_removed_cats() {
+ return (apol_string_vector_t*)poldiff_range_get_min_removed_cats(self);
+ };
+ const apol_string_vector_t *get_min_unmodified_cats() {
+ return (apol_string_vector_t*)poldiff_range_get_min_unmodified_cats(self);
+ };
+};
+%inline %{
+ poldiff_range_t *poldiff_range_from_void(void *x) {
+ return (poldiff_range_t*)x;
+ };
+%}
+
+/* range_transition rule diff */
+typedef struct poldiff_range_trans {} poldiff_range_trans_t;
+%extend poldiff_range_trans_t {
+ poldiff_range_trans_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_range_trans_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_range_trans_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_range_trans_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ poldiff_form_e get_form() {
+ return poldiff_range_trans_get_form(self);
+ };
+ const char *get_source_type() {
+ return poldiff_range_trans_get_source_type(self);
+ };
+ const char *get_target_type() {
+ return poldiff_range_trans_get_target_type(self);
+ };
+ const char *get_target_class() {
+ return poldiff_range_trans_get_target_class(self);
+ };
+ const poldiff_range_t *get_range() {
+ return poldiff_range_trans_get_range(self);
+ };
+};
+%inline %{
+ poldiff_range_trans_t *poldiff_range_trans_from_void(void *x) {
+ return (poldiff_range_trans_t *)x;
+ };
+%}
+
+/* role allow rule diff */
+typedef struct poldiff_role_allow {} poldiff_role_allow_t;
+%extend poldiff_role_allow_t {
+ poldiff_role_allow_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_role_allow_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_role_allow_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_role_allow_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_role_allow_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_role_allow_get_form(self);
+ };
+ const apol_string_vector_t *get_unmodified_roles() {
+ return (apol_string_vector_t*)poldiff_role_allow_get_unmodified_roles(self);
+ };
+ const apol_string_vector_t *get_added_roles() {
+ return (apol_string_vector_t*)poldiff_role_allow_get_added_roles(self);
+ };
+ const apol_string_vector_t *get_removed_roles() {
+ return (apol_string_vector_t*)poldiff_role_allow_get_removed_roles(self);
+ };
+};
+%inline %{
+ poldiff_role_allow_t *poldiff_role_allow_from_void(void *x) {
+ return (poldiff_role_allow_t *)x;
+ };
+%}
+
+/* role_transition rule diff */
+typedef struct poldiff_role_trans {} poldiff_role_trans_t;
+%extend poldiff_role_trans_t {
+ poldiff_role_trans_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_role_trans_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_role_trans_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_role_trans_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ poldiff_form_e get_form() {
+ return poldiff_role_trans_get_form(self);
+ };
+ const char *get_source_role() {
+ return poldiff_role_trans_get_source_role(self);
+ };
+ const char *get_target_type() {
+ return poldiff_role_trans_get_target_type(self);
+ };
+ const char *get_original_default() {
+ return poldiff_role_trans_get_original_default(self);
+ };
+ const char *get_modified_default() {
+ return poldiff_role_trans_get_modified_default(self);
+ };
+};
+%inline %{
+ poldiff_role_trans_t *poldiff_role_trans_from_void(void *x) {
+ return (poldiff_role_trans_t *)x;
+ };
+%}
+
+/* role diff */
+typedef struct poldiff_role {} poldiff_role_t;
+%extend poldiff_role_t {
+ poldiff_role_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_role_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_role_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_role_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_role_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_role_get_form(self);
+ };
+ const apol_string_vector_t *get_added_types() {
+ return (apol_string_vector_t*)poldiff_role_get_added_types(self);
+ };
+ const apol_string_vector_t *get_removed_types() {
+ return (apol_string_vector_t*)poldiff_role_get_removed_types(self);
+ };
+};
+%inline %{
+ poldiff_role_t *poldiff_role_from_void(void *x) {
+ return (poldiff_role_t*)x;
+ };
+%}
+
+/* te rule diff */
+typedef struct poldiff_terule {} poldiff_terule_t;
+%extend poldiff_terule_t {
+ poldiff_terule_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_terule_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_terule_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_terule_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ poldiff_form_e get_form() {
+ return poldiff_terule_get_form(self);
+ };
+ uint32_t get_rule_type() {
+ return poldiff_terule_get_rule_type(self);
+ };
+ const char *get_source_type() {
+ return poldiff_terule_get_source_type(self);
+ };
+ const char *get_target_type() {
+ return poldiff_terule_get_target_type(self);
+ };
+ const char *get_object_class() {
+ return poldiff_terule_get_object_class(self);
+ };
+ const qpol_cond_t *get_cond(poldiff_t *p) {
+ const qpol_cond_t *cond;
+ uint32_t which_list;
+ const apol_policy_t *which_pol;
+ poldiff_terule_get_cond(p, self, &cond, &which_list, &which_pol);
+ return cond;
+ };
+ uint32_t get_cond_list(poldiff_t *p) {
+ const qpol_cond_t *cond;
+ uint32_t which_list;
+ const apol_policy_t *which_pol;
+ poldiff_terule_get_cond(p, self, &cond, &which_list, &which_pol);
+ return which_list;
+ };
+ const apol_policy_t *get_cond_policy(poldiff_t *p) {
+ const qpol_cond_t *cond;
+ uint32_t which_list;
+ const apol_policy_t *which_pol;
+ poldiff_terule_get_cond(p, self, &cond, &which_list, &which_pol);
+ return which_pol;
+ };
+ const char *get_original_default() {
+ return poldiff_terule_get_original_default(self);
+ };
+ const char *get_modified_default() {
+ return poldiff_terule_get_modified_default(self);
+ };
+ const apol_vector_t *get_orig_line_numbers() {
+ return poldiff_terule_get_orig_line_numbers(self);
+ };
+ const apol_vector_t *get_mod_line_numbers() {
+ return poldiff_terule_get_mod_line_numbers(self);
+ };
+};
+%inline %{
+ poldiff_terule_t *poldiff_terule_from_void(void *x) {
+ return (poldiff_terule_t*)x;
+ };
+%}
+
+/* type diff */
+typedef struct poldiff_type {} poldiff_type_t;
+%extend poldiff_type_t {
+ poldiff_type_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_type_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_type_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_type_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_type_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_type_get_form(self);
+ };
+ const apol_string_vector_t *get_added_attribs() {
+ return (apol_string_vector_t*)poldiff_type_get_added_attribs(self);
+ };
+ const apol_string_vector_t *get_removed_attribs() {
+ return (apol_string_vector_t*)poldiff_type_get_removed_attribs(self);
+ };
+};
+%inline %{
+ poldiff_type_t *poldiff_type_from_void(void *x) {
+ return (poldiff_type_t*)x;
+ };
+%}
+
+/* user diff */
+typedef struct poldiff_user {} poldiff_user_t;
+%extend poldiff_user_t {
+ poldiff_user_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_user_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_user_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_user_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_user_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_user_get_form(self);
+ };
+ const apol_string_vector_t *get_unmodified_roles() {
+ return (apol_string_vector_t*)poldiff_user_get_unmodified_roles(self);
+ };
+ const apol_string_vector_t *get_added_roles() {
+ return (apol_string_vector_t*)poldiff_user_get_added_roles(self);
+ };
+ const apol_string_vector_t *get_removed_roles() {
+ return (apol_string_vector_t*)poldiff_user_get_removed_roles(self);
+ };
+ const poldiff_level_t *get_original_dfltlevel() {
+ return poldiff_user_get_original_dfltlevel(self);
+ };
+ const poldiff_level_t *get_modified_dfltlevel() {
+ return poldiff_user_get_modified_dfltlevel(self);
+ };
+ const poldiff_range_t *get_range() {
+ return poldiff_user_get_range(self);
+ };
+};
+%inline %{
+ poldiff_user_t *poldiff_user_from_void(void *x) {
+ return (poldiff_user_t*)x;
+ };
+%}
+
+/* type remap */
+typedef struct poldiff_type_remap_entry {} poldiff_type_remap_entry_t;
+%extend poldiff_type_remap_entry_t {
+ poldiff_type_remap_entry_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_type_remap_entry_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_type_remap_entry_t() {
+ /* no op */
+ return;
+ };
+ %newobject get_original_types(poldiff_t*);
+ apol_string_vector_t *get_original_types(poldiff_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ v = poldiff_type_remap_entry_get_original_types(p, self);
+ if (!v) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return (apol_string_vector_t*)v;
+ };
+ %newobject get_modified_types(poldiff_t*);
+ apol_string_vector_t *get_modified_types(poldiff_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ v = poldiff_type_remap_entry_get_modified_types(p, self);
+ if (!v) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return (apol_string_vector_t*)v;
+ };
+ int get_is_inferred() {
+ return poldiff_type_remap_entry_get_is_inferred(self);
+ };
+ int get_is_enabled() {
+ return poldiff_type_remap_entry_get_is_enabled(self);
+ };
+ void set_enabled(int enable) {
+ poldiff_type_remap_entry_set_enabled(self, enable);
+ };
+};
+%inline %{
+ poldiff_type_remap_entry_t *poldiff_type_remap_entry_from_void(void *x) {
+ return (poldiff_type_remap_entry_t*)x;
+ };
+%}
+
diff --git a/libpoldiff/swig/python/Makefile.am b/libpoldiff/swig/python/Makefile.am
new file mode 100644
index 0000000..ea3ea85
--- /dev/null
+++ b/libpoldiff/swig/python/Makefile.am
@@ -0,0 +1,39 @@
+wrappedso_DATA = _poldiff.so.@libpoldiff_version@
+wrappedso_SONAME = @libpoldiff_pyswig_soname@
+wrappedsodir = $(pkgpyexecdir)
+
+wrappedpy_DATA = poldiff.py
+wrappedpydir = $(pkgpyexecdir)
+
+dist_noinst_DATA = $(srcdir)/../poldiff.i
+BUILT_SOURCES = poldiff_wrap.c
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libpoldiff/include
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \
+ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @POLDIFF_LIB_FLAG@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libpoldiff/src/libpoldiff.so
+
+$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) $(SWIG_PYTHON_OPT) -o $@ \
+ -I$(top_srcdir)/libpoldiff/include -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/include \
+ -I$(top_srcdir)/libqpol/swig -I$(top_srcdir)/libapol/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 $@ _poldiff.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) _poldiff.so
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(wrappedsodir)/$(wrappedso_SONAME) $(DESTDIR)$(wrappedsodir)/_poldiff.so
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) $(wrappedpy_DATA) $(wrappedso_SONAME) _poldiff.so poldiff.pyc
diff --git a/libpoldiff/swig/tcl/Makefile.am b/libpoldiff/swig/tcl/Makefile.am
new file mode 100644
index 0000000..d4bcdc9
--- /dev/null
+++ b/libpoldiff/swig/tcl/Makefile.am
@@ -0,0 +1,37 @@
+wrappedso_DATA = libtpoldiff.so.@libpoldiff_version@
+wrappedso_SONAME = @libpoldiff_tswig_soname@
+short_name = libtpoldiff.so
+wrappedsodir = $(libdir)/setools/poldiff
+
+package_SCRIPTS = pkgIndex.tcl
+packagedir = $(wrappedsodir)
+
+dist_noinst_DATA = $(srcdir)/../poldiff.i
+BUILT_SOURCES = poldiff_wrap.c
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libpoldiff/include
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \
+ @POLDIFF_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libpoldiff/src/libpoldiff.so
+
+$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) $(SWIG_TCL_OPT) -pkgversion @libpoldiff_version@ -o $@ -I$(top_srcdir)/libpoldiff/include -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libapol/swig -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:$(top_builddir)/libpoldiff/src $(TCLSH_PROG)
+ chmod 644 $@
+ $(mkdir_p) poldiff
+ cp $(wrappedso_DATA) $@ poldiff
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) $(wrappedso_SONAME) $(short_name) $(package_DATA) poldiff/$(wrappedso_DATA) poldiff/$(package_SCRIPTS)
+
+CLEANFILES = $(package_SCRIPTS)
diff --git a/libpoldiff/tests/Makefile.am b/libpoldiff/tests/Makefile.am
new file mode 100644
index 0000000..f46f739
--- /dev/null
+++ b/libpoldiff/tests/Makefile.am
@@ -0,0 +1,19 @@
+TESTS = libpoldiff-tests
+check_PROGRAMS = libpoldiff-tests
+
+libpoldiff_tests_SOURCES = \
+ components-tests.c components-tests.h \
+ libpoldiff-tests.c libpoldiff-tests.h \
+ mls-tests.c mls-tests.h \
+ nomls-tests.c nomls-tests.h \
+ policy-defs.h \
+ rules-tests.c rules-tests.h
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ @POLDIFF_CFLAGS@
+
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+LDADD = @SELINUX_LIB_FLAG@ @POLDIFF_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @CUNIT_LIB_FLAG@
+
+libpoldiff_tests_DEPENDENCIES = ../src/libpoldiff.so
diff --git a/libpoldiff/tests/components-tests.c b/libpoldiff/tests/components-tests.c
new file mode 100644
index 0000000..4d70e80
--- /dev/null
+++ b/libpoldiff/tests/components-tests.c
@@ -0,0 +1,544 @@
+/**
+ * @file
+ *
+ * Test the libpoldiff's correctness for components.
+ *
+ * @author Paul Rosenfeld prosenfeld@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 "libpoldiff-tests.h"
+#include "components-tests.h"
+#include "policy-defs.h"
+#include <CUnit/Basic.h>
+#include <CUnit/TestDB.h>
+
+#include <apol/util.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *unchanged_attributes[] = {
+/* 00.0 */
+ "data",
+ NULL
+};
+char *added_attributes[] = {
+/* 00.1 */
+ "mineral",
+ NULL
+};
+char *removed_attributes[] = {
+/* 00.2 */
+ "other",
+ NULL
+};
+char *modified_attributes[] = {
+/* 00.3.0 */
+ "tree +holly_t",
+/* 00.3.1 */
+ "fish -bass_t",
+ "plant -daikon_t",
+/* 00.3.2 */
+ "animal +hippo_t",
+ "animal -bass_t",
+ "animal -koala_t",
+ "mammal +hippo_t",
+ "mammal -bear_t",
+ NULL
+};
+char *unchanged_bools[] = {
+/* 02.0 */
+ "frog",
+ NULL
+};
+char *added_bools[] = {
+/* 02.1 */
+ "shark",
+ NULL
+};
+char *removed_bools[] = {
+/* 02.2 */
+ "dog",
+ NULL
+};
+char *modified_bools[] = {
+/* 02.3 */
+ "wark",
+ NULL
+};
+char *unchanged_classes[] = {
+/* 04.0 */
+ "filesystem", "dir", "blk_file", "sock_file", "fifo_file", "netif",
+ "process", "msg", "security", "system", "capability", "passwd",
+ "window", "font", "colormap", "property", "cursor", "xclient",
+ "xinput", "xserver", "xextension", "pax", "dbus", "ncsd",
+ "association", "context", NULL
+};
+char *added_classes[] = {
+/* 04.1 */
+ "thing",
+ NULL
+};
+char *removed_classes[] = {
+/* 04.2 */
+ "key",
+ NULL
+};
+char *modified_classes[] = {
+/* 04.3.00 */
+ "fd +be",
+/* 04.3.01 */
+ "chr_file -execmod",
+/* 04.3.02*/
+ "file +newperm",
+ "file -execmod",
+/* 04.3.03 */
+ "ipc +unix_exec",
+ "sem +unix_exec",
+/* 04.3.04 */
+ "socket -name_bind",
+ "tcp_socket -name_bind",
+ "udp_socket -name_bind",
+ "netlink_socket -name_bind",
+ "packet_socket -name_bind",
+ "key_socket -name_bind",
+ "unix_dgram_socket -name_bind",
+ "dccp_socket -name_bind",
+ "netlink_route_socket -name_bind",
+ "netlink_firewall_socket -name_bind",
+ "netlink_tcpdiag_socket -name_bind",
+ "netlink_nflog_socket -name_bind",
+ "netlink_xfrm_socket -name_bind",
+ "netlink_selinux_socket -name_bind",
+ "netlink_audit_socket -name_bind",
+ "netlink_ip6fw_socket -name_bind",
+ "netlink_dnrt_socket -name_bind",
+ "appletalk_socket -name_bind",
+ "netlink_kobject_uevent_socket -name_bind",
+/* 04.3.05 */
+ "drawable +bar",
+ "drawable -blah",
+/* 04.3.06 */
+ "msgq +unix_exec",
+ "msgq +dequeue",
+/* 04.3.07 */
+ "rawip_socket -name_bind",
+ "rawip_socket +ip_bind",
+/* 04.3.08 */
+ "shm +unix_exec",
+ "shm -lock",
+/* 04.3.09 */
+ "unix_stream_socket -newconn",
+ "unix_stream_socket -name_bind",
+/* 04.3.10 */
+ "gc +bar",
+ "gc +remove",
+ "gc -blah",
+ "gc -free",
+ NULL
+};
+
+char *unchanged_commons[] = {
+/* 05.0 */
+ "file",
+ NULL
+};
+char *added_commons[] = {
+/* 05.1 */
+ "new",
+ NULL
+};
+char *removed_commons[] = {
+/* 05.2 */
+ "old",
+ NULL
+};
+char *modified_commons[] = {
+/* 05.3.0 */
+ "ipc +unix_exec",
+/* 05.3.1 */
+ "socket -name_bind",
+/* 05.3.2 */
+ "bob -blah",
+ "bob +bar",
+ NULL
+};
+
+char *unchanged_roles[] = {
+/* 08.0 */
+ "placeholder_r", "admin_r", "intern_r",
+ NULL
+};
+char *added_roles[] = {
+/* 08.1 */
+ "strange_r",
+ NULL
+};
+char *removed_roles[] = {
+/* 08.2 */
+ "guest_r",
+ NULL
+};
+char *modified_roles[] = {
+/* 08.3.0 */
+ "user_r +hippo_t",
+/* 08.3.1 */
+ "lumberjack_r +holly_t",
+/* 08.3.2 */
+ "staff_r -bass_t",
+/* 08.3.3 */
+ "aquarium_r -bass_t",
+ "garden_r -daikon_t",
+/* 08.3.4 */
+ "object_r +hippo_t",
+ "object_r +acorn_t",
+ "object_r -bass_t",
+ "object_r -koala_t",
+ "deity_r +acorn_t",
+ "deity_r +hippo_t",
+ "deity_r -bass_t",
+ "deity_r -dirt_t",
+ "deity_r -koala_t",
+/* 08.3.5 */
+ "zoo_r +hippo_t",
+ "zoo_r -bass_t",
+ "zoo_r -koala_t",
+ "mammal_r +hippo_t",
+ "mammal_r -bear_t",
+ NULL
+};
+
+char *unchanged_types[] = {
+/* 12.0.0 */
+ "placeholder_t", "finch_t", "trout_t",
+ "birch_t", "oak_t", "potato_t", "tiger_t",
+ "lion_t", "pine_t", "log_t", "file_t",
+/* 12.0.1 */
+ "firefly_t", "lightningbug_t",
+/* 12.0.2 */
+ "rock_t", "big_stone_t",
+ NULL
+};
+
+char *added_types[] = {
+/* 12.1.0 */
+ "hippo_t",
+ "acorn_t",
+ NULL
+};
+
+/* 12.1.1 */
+char *removed_types[] = {
+/* 12.2.0 */
+ "bass_t",
+/* 12.2.1 */
+ "koala_t",
+ NULL
+};
+
+char *modified_types[] = {
+/* 12.3.0 */
+ "holly_t +tree",
+/* 12.3.1 */
+ "bear_t -mammal",
+/* 12.3.2 */
+ "daikon_t -plant",
+ "daikon_t +mineral",
+/* 12.3.3 */
+ "glass_t -> crystal_t +mineral",
+/* 12.3.4 */
+ "dirt_t -> soil_t +mineral",
+/* NEED TO BE ADDED */
+ "stone_t -other",
+ "system_t -other",
+ NULL
+};
+char *aliased_types[] = {
+ /* 12.2.1 */
+ "bear_t -> koala_t",
+ NULL
+};
+
+char *unchanged_users[] = {
+/* 13.0 */
+ "placeholder_u", "su_u", "cyn_u", "danika_u",
+ NULL
+};
+char *added_users[] = {
+/* 13.1 */
+ "gai_u",
+ NULL
+};
+char *removed_users[] = {
+/* 13.2 */
+ "mehnlo_u",
+ NULL
+};
+char *modified_users[] = {
+/* 13.3.0 */
+ "devona_u +aquarium_r",
+ "eve_u +strange_r",
+/* 13.3.1 */
+ "nika_u -user_r",
+/* 13.3.2 */
+ "meloni_u +garden_r",
+ "meloni_u -user_r",
+ NULL
+};
+
+/* This #define is kind of like a template since all of the "get_name" classes
+ * follow the same pattern. The wrapped function name comes out the same as the
+ * original, but with a _w at the end (for example: poldiff_attribute_get_name_w
+ * see definition in components-tests.h */
+WRAP_NAME_FUNC(attrib)
+ WRAP_NAME_FUNC(bool)
+ WRAP_NAME_FUNC(class)
+ WRAP_NAME_FUNC(common)
+ WRAP_NAME_FUNC(role)
+ WRAP_NAME_FUNC(type)
+ WRAP_NAME_FUNC(user)
+ WRAP_NAME_FUNC(cat)
+/* This is the same idea except for with "get_added" and "get_removed" */
+ WRAP_MOD_FUNC(class, perms, added)
+ WRAP_MOD_FUNC(class, perms, removed)
+ WRAP_MOD_FUNC(attrib, types, added)
+ WRAP_MOD_FUNC(attrib, types, removed)
+ WRAP_MOD_FUNC(common, perms, added)
+ WRAP_MOD_FUNC(common, perms, removed)
+ WRAP_MOD_FUNC(role, types, added)
+ WRAP_MOD_FUNC(role, types, removed)
+ WRAP_MOD_FUNC(user, roles, added)
+ WRAP_MOD_FUNC(user, roles, removed)
+ WRAP_MOD_FUNC(type, attribs, added)
+ WRAP_MOD_FUNC(type, attribs, removed)
+
+void build_component_vecs(component_funcs_t * component_funcs)
+{
+ size_t i;
+ const void *item = NULL;
+ const apol_vector_t *v = NULL;
+ v = component_funcs->get_diff_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ item = apol_vector_get_element(v, i);
+ const char *name_only = NULL;
+ name_only = component_funcs->get_name(item);
+ if (component_funcs->get_form(item) == POLDIFF_FORM_ADDED) {
+ apol_vector_append(added_v, strdup(name_only));
+ } else if (component_funcs->get_form(item) == POLDIFF_FORM_REMOVED) {
+ apol_vector_append(removed_v, strdup(name_only));
+ } else if (component_funcs->get_form(item) == POLDIFF_FORM_MODIFIED) {
+ apol_vector_append(modified_name_only_v, strdup(name_only));
+ size_t j;
+ if (component_funcs->get_added) {
+ const apol_vector_t *added_elements = component_funcs->get_added(item);
+ for (j = 0; j < apol_vector_get_size(added_elements); ++j) {
+ char *added_element;
+ added_element = apol_vector_get_element(added_elements, j);
+ char *modification_str = NULL;
+ size_t modification_str_len = 0;
+ apol_str_appendf(&modification_str, &modification_str_len, "%s %s%s", name_only, "+",
+ added_element);
+ apol_vector_append(modified_v, modification_str);
+ }
+ }
+ if (component_funcs->get_removed) {
+ const apol_vector_t *removed_elements = component_funcs->get_removed(item);
+ for (j = 0; j < apol_vector_get_size(removed_elements); ++j) {
+ char *removed_element;
+ removed_element = apol_vector_get_element(removed_elements, j);
+ char *modification_str = NULL;
+ size_t modification_str_len = 0;
+ apol_str_appendf(&modification_str, &modification_str_len, "%s %s%s", name_only, "-",
+ removed_element);
+ apol_vector_append(modified_v, modification_str);
+ }
+ }
+ if (!(component_funcs->get_added && component_funcs)) {
+ apol_vector_append(modified_v, strdup(name_only));
+ }
+ }
+ }
+}
+
+void components_types_tests()
+{
+ poldiff_test_answers_t *answers = init_answer_vectors(added_types, removed_types, unchanged_types, modified_types);
+ component_funcs_t *funcs = init_test_funcs(poldiff_get_type_vector, poldiff_type_get_name_w,
+ poldiff_type_get_form, poldiff_type_get_added_attribs_w,
+ poldiff_type_get_removed_attribs_w);
+ run_test(funcs, answers, COMPONENT);
+ free(funcs);
+ /* this is for the alias tests */
+ size_t i;
+ apol_vector_t *orig_aliases_v = apol_vector_create(free);
+ apol_vector_t *mod_aliases_v = apol_vector_create(free);
+ apol_vector_t *final_aliases_v = apol_vector_create(free);
+ apol_vector_t *correct_final_aliases_v = string_array_to_vector(aliased_types);
+ apol_vector_t *changed_aliases_v;
+
+ qpol_policy_t *orig_qpolicy = apol_policy_get_qpol(orig_policy);
+ qpol_policy_t *mod_qpolicy = apol_policy_get_qpol(mod_policy);
+
+ qpol_iterator_t *orig_types;
+ qpol_iterator_t *mod_types;
+
+ qpol_policy_get_type_iter(mod_qpolicy, &orig_types);
+ for (; !qpol_iterator_end(orig_types); qpol_iterator_next(orig_types)) {
+ unsigned char isalias = 0;
+ qpol_type_t *qpol_type;
+ const char *name;
+ qpol_iterator_get_item(orig_types, (void **)&qpol_type);
+ qpol_type_get_name(orig_qpolicy, qpol_type, &name);
+ qpol_type_get_isalias(orig_qpolicy, qpol_type, &isalias);
+ if (!isalias) {
+ apol_vector_append(orig_aliases_v, strdup(name));
+ }
+ }
+ qpol_policy_get_type_iter(mod_qpolicy, &mod_types);
+ for (; !qpol_iterator_end(mod_types); qpol_iterator_next(mod_types)) {
+ unsigned char isalias = 0;
+ const qpol_type_t *qpol_type;
+ const char *name;
+ qpol_iterator_get_item(mod_types, (void **)&qpol_type);
+ qpol_type_get_name(mod_qpolicy, qpol_type, &name);
+ qpol_type_get_isalias(mod_qpolicy, qpol_type, &isalias);
+ if (isalias) {
+ apol_vector_append(mod_aliases_v, strdup(name));
+ }
+ }
+
+ changed_aliases_v = apol_vector_create_from_intersection(orig_aliases_v, mod_aliases_v, apol_str_strcmp, NULL);
+ char *alias_str = NULL, *str = NULL;
+ size_t alias_str_len = 0, str_len = 0;
+ for (i = 0; i < apol_vector_get_size(changed_aliases_v); ++i) {
+ char *name = apol_vector_get_element(changed_aliases_v, i);
+ qpol_iterator_t *aliased_to;
+ const qpol_type_t *qtype;
+ qpol_policy_get_type_by_name(mod_qpolicy, name, &qtype);
+ qpol_type_get_alias_iter(mod_qpolicy, qtype, &aliased_to);
+ for (; !qpol_iterator_end(aliased_to); qpol_iterator_next(aliased_to)) {
+ const char *name;
+ qpol_iterator_get_item(aliased_to, (void **)&name);
+ apol_str_append(&alias_str, &alias_str_len, name);
+ }
+ apol_str_appendf(&str, &str_len, "%s -> %s", name, alias_str);
+ free(alias_str);
+ apol_vector_append(final_aliases_v, str);
+ qpol_iterator_destroy(&aliased_to);
+ }
+ apol_vector_sort(final_aliases_v, compare_str, NULL);
+ apol_vector_sort(correct_final_aliases_v, compare_str, NULL);
+ size_t first_diff = 0;
+ int test_result;
+
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(final_aliases_v, correct_final_aliases_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(final_aliases_v, correct_final_aliases_v, first_diff, "Aliases");
+ }
+ apol_vector_destroy(&orig_aliases_v);
+ apol_vector_destroy(&mod_aliases_v);
+ apol_vector_destroy(&final_aliases_v);
+ apol_vector_destroy(&correct_final_aliases_v);
+ apol_vector_destroy(&changed_aliases_v);
+ qpol_iterator_destroy(&mod_types);
+ qpol_iterator_destroy(&orig_types);
+
+ cleanup_test(answers);
+}
+
+void components_bools_tests()
+{
+ poldiff_test_answers_t *answers = init_answer_vectors(added_bools, removed_bools, unchanged_bools, modified_bools);
+ component_funcs_t *funcs = init_test_funcs(poldiff_get_bool_vector, poldiff_bool_get_name_w,
+ poldiff_bool_get_form, NULL, NULL);
+ run_test(funcs, answers, COMPONENT);
+ free(funcs);
+ cleanup_test(answers);
+}
+
+void components_users_tests()
+{
+ poldiff_test_answers_t *answers = init_answer_vectors(added_users, removed_users, unchanged_users, modified_users);
+ component_funcs_t *funcs = init_test_funcs(poldiff_get_user_vector, poldiff_user_get_name_w,
+ poldiff_user_get_form, poldiff_user_get_added_roles_w,
+ poldiff_user_get_removed_roles_w);
+ run_test(funcs, answers, COMPONENT);
+ free(funcs);
+ cleanup_test(answers);
+}
+
+void components_roles_tests()
+{
+ poldiff_test_answers_t *answers = init_answer_vectors(added_roles, removed_roles, unchanged_roles, modified_roles);
+ component_funcs_t *funcs = init_test_funcs(poldiff_get_role_vector, poldiff_role_get_name_w, poldiff_role_get_form,
+ poldiff_role_get_added_types_w, poldiff_role_get_removed_types_w);
+ run_test(funcs, answers, COMPONENT);
+ free(funcs);
+ cleanup_test(answers);
+}
+
+void components_commons_tests()
+{
+ poldiff_test_answers_t *answers = init_answer_vectors(added_commons, removed_commons, unchanged_commons, modified_commons);
+ component_funcs_t *funcs = init_test_funcs(poldiff_get_common_vector, poldiff_common_get_name_w, poldiff_common_get_form,
+ poldiff_common_get_added_perms_w, poldiff_common_get_removed_perms_w);
+ run_test(funcs, answers, COMPONENT);
+ free(funcs);
+ cleanup_test(answers);
+}
+
+void components_attributes_tests()
+{
+ poldiff_test_answers_t *answers =
+ init_answer_vectors(added_attributes, removed_attributes, unchanged_attributes, modified_attributes);
+ component_funcs_t *funcs = init_test_funcs(poldiff_get_attrib_vector, poldiff_attrib_get_name_w,
+ poldiff_attrib_get_form, poldiff_attrib_get_added_types_w,
+ poldiff_attrib_get_removed_types_w);
+
+ run_test(funcs, answers, COMPONENT);
+ free(funcs);
+ cleanup_test(answers);
+}
+
+void components_class_tests()
+{
+ poldiff_test_answers_t *answers = init_answer_vectors(added_classes, removed_classes, unchanged_classes, modified_classes);
+ component_funcs_t *funcs = init_test_funcs(poldiff_get_class_vector, poldiff_class_get_name_w,
+ poldiff_class_get_form, poldiff_class_get_added_perms_w,
+ poldiff_class_get_removed_perms_w);
+ run_test(funcs, answers, COMPONENT);
+ free(funcs);
+ cleanup_test(answers);
+}
+
+int components_test_init()
+{
+ if (!(diff = init_poldiff(COMPONENTS_ORIG_POLICY, COMPONENTS_MOD_POLICY))) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
diff --git a/libpoldiff/tests/components-tests.h b/libpoldiff/tests/components-tests.h
new file mode 100644
index 0000000..8d8ed6f
--- /dev/null
+++ b/libpoldiff/tests/components-tests.h
@@ -0,0 +1,49 @@
+/**
+ * @file
+ *
+ * Header file for libpoldiff's correctness of components.
+ *
+ * @author Paul Rosenfeld prosenfeld@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 COMPONENTS_TEST
+#define COMPONENTS_TEST
+
+#define WRAP_NAME_FUNC(component) const char *poldiff_##component##_get_name_w(const void *arg) { \
+ const poldiff_##component##_t *cls = (const poldiff_##component##_t *)arg; \
+ return poldiff_##component##_get_name(cls); }
+
+#define WRAP_MOD_FUNC(component,mod_component,mod_type) const apol_vector_t* poldiff_##component##_get_##mod_type##_##mod_component##_w(const void* arg) { \
+ const poldiff_##component##_t *cls = (const poldiff_##component##_t *)arg; \
+ return poldiff_##component##_get_##mod_type##_##mod_component(cls); }
+
+void build_component_vecs(component_funcs_t *);
+
+int components_test_init();
+int components_test_cleanup();
+
+void components_attributes_tests();
+void components_bools_tests();
+void components_commons_tests();
+void components_roles_tests();
+void components_users_tests();
+void components_class_tests();
+void components_types_tests();
+
+#endif
diff --git a/libpoldiff/tests/libpoldiff-tests.c b/libpoldiff/tests/libpoldiff-tests.c
new file mode 100644
index 0000000..c665c1b
--- /dev/null
+++ b/libpoldiff/tests/libpoldiff-tests.c
@@ -0,0 +1,364 @@
+/**
+ * @file
+ *
+ * CUnit testing framework for libpoldiff's correctness.
+ *
+ * @author Paul Rosenfeld prosenfeld@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 "libpoldiff-tests.h"
+
+#include <CUnit/Basic.h>
+#include <CUnit/TestDB.h>
+
+#include <apol/util.h>
+#include <apol/vector.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "components-tests.h"
+#include "rules-tests.h"
+#include "mls-tests.h"
+#include "nomls-tests.h"
+
+apol_vector_t *string_array_to_vector(char *arr[])
+{
+ apol_vector_t *v = apol_vector_create(free);
+ int i;
+ for (i = 0; arr[i] != NULL; ++i) {
+ apol_vector_append(v, strdup(arr[i]));
+ }
+ return v;
+}
+
+char *vector_to_string(const apol_vector_t * v, const char *pre, const char *sep)
+{
+ char *item = NULL, *str = NULL, *tmp = NULL;
+ size_t i = 0, str_len = 0, tmp_len = 0;
+ size_t num_elements = apol_vector_get_size(v);
+ for (i = 0; v && i < num_elements; i++) {
+ item = apol_vector_get_element(v, i);
+ if (apol_str_appendf(&tmp, &tmp_len, "%s%s", sep, item) < 0) {
+ return NULL;
+ }
+ }
+ apol_str_trim(tmp);
+ if (tmp) {
+ apol_str_appendf(&str, &str_len, "%s%s", pre, tmp);
+ } else {
+ str = strdup("");
+ }
+ free(tmp);
+ return str;
+}
+
+apol_vector_t *shallow_copy_str_vec_and_sort(const apol_vector_t * v)
+{
+ apol_vector_t *copy = apol_vector_create_from_vector(v, NULL, NULL, NULL);
+ apol_vector_sort(copy, apol_str_strcmp, NULL);
+ return copy;
+}
+
+void run_test(component_funcs_t * component_funcs, poldiff_test_answers_t * poldiff_test_answers, test_numbers_e test_num)
+{
+ added_v = apol_vector_create(free);
+ removed_v = apol_vector_create(free);
+ modified_v = apol_vector_create(free);
+ modified_name_only_v = apol_vector_create(free);
+ switch (test_num) {
+ case COMPONENT:
+ build_component_vecs(component_funcs);
+ break;
+ case RULES_AVRULE:
+ build_avrule_vecs();
+ break;
+ case RULES_TERULE:
+ build_terule_vecs();
+ break;
+ case RULES_ROLEALLOW:
+ build_roleallow_vecs();
+ break;
+ case RULES_ROLETRANS:
+ build_roletrans_vecs();
+ break;
+ case MLS_CATEGORY:
+ build_category_vecs();
+ break;
+ case MLS_LEVEL:
+ build_level_vecs();
+ break;
+ case MLS_RANGETRANS:
+ build_rangetrans_vecs();
+ break;
+ case MLS_USER:
+ build_user_vecs();
+ break;
+ }
+ size_t first_diff;
+ apol_vector_t *intersect = NULL, *all_changes = NULL;
+ if (!(all_changes = apol_vector_create(NULL))) {
+ goto err;
+ }
+ apol_vector_cat(all_changes, added_v);
+ apol_vector_cat(all_changes, removed_v);
+ apol_vector_cat(all_changes, modified_name_only_v);
+ if (!
+ (intersect =
+ apol_vector_create_from_intersection(all_changes, poldiff_test_answers->correct_unchanged_v, compare_str, NULL))) {
+ goto err;
+ }
+ /* unchanged */
+ CU_ASSERT_EQUAL(apol_vector_get_size(intersect), 0);
+ /* added */
+ apol_vector_sort(added_v, compare_str, NULL);
+ apol_vector_sort(poldiff_test_answers->correct_added_v, compare_str, NULL);
+ int test_result;
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(added_v, poldiff_test_answers->correct_added_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(added_v, poldiff_test_answers->correct_added_v, first_diff, "Added");
+ }
+ /* removed */
+ apol_vector_sort(removed_v, compare_str, NULL);
+ apol_vector_sort(poldiff_test_answers->correct_removed_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(removed_v, poldiff_test_answers->correct_removed_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(removed_v, poldiff_test_answers->correct_removed_v, first_diff, "Removed");
+ }
+ /* modified */
+ apol_vector_sort(modified_v, compare_str, NULL);
+ apol_vector_sort(poldiff_test_answers->correct_modified_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(modified_v, poldiff_test_answers->correct_modified_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(modified_v, poldiff_test_answers->correct_modified_v, first_diff, "Modified");
+ }
+
+ apol_vector_destroy(&intersect);
+ apol_vector_destroy(&added_v);
+ apol_vector_destroy(&removed_v);
+ apol_vector_destroy(&modified_name_only_v);
+ apol_vector_destroy(&modified_v);
+ apol_vector_destroy(&all_changes);
+ return;
+ err:
+ apol_vector_destroy(&intersect);
+ apol_vector_destroy(&added_v);
+ apol_vector_destroy(&removed_v);
+ apol_vector_destroy(&modified_name_only_v);
+ apol_vector_destroy(&modified_v);
+ apol_vector_destroy(&all_changes);
+ CU_FAIL_FATAL("Could not initialize vectors for test");
+}
+
+void print_test_failure(apol_vector_t * actual, apol_vector_t * expected, size_t first_diff, const char *test_name)
+{
+ printf("\nTEST FAILED\n");
+ size_t i;
+ printf("--- ACTUAL RESULT (%s) -----\n", test_name);
+ for (i = first_diff; i < apol_vector_get_size(actual); ++i) {
+ char *item = (char *)apol_vector_get_element(actual, i);
+ printf("\t%3d. %s\n", (int)i, item);
+ }
+ printf("--- EXPECTED RESULT (%s) ---\n", test_name);
+ for (i = first_diff; i < apol_vector_get_size(expected); ++i) {
+ char *item = (char *)apol_vector_get_element(expected, i);
+ printf("\t%3d. %s\n", (int)i, item);
+ }
+}
+
+int compare_str(const void *s1, const void *s2, void *debug)
+{
+ char *str1 = strdup((char *)s1);
+ char *str2 = strdup((char *)s2);
+ apol_str_trim(str1);
+ apol_str_trim(str2);
+ int result = strcmp(str1, str2);
+ free(str1);
+ free(str2);
+ return result;
+}
+
+poldiff_test_answers_t *init_answer_vectors(char *added_arr[], char *removed_arr[], char *unchanged_arr[], char *modified_arr[])
+{
+ poldiff_test_answers_t *answers = (poldiff_test_answers_t *) malloc(sizeof(poldiff_test_answers_t));
+ answers->correct_added_v = string_array_to_vector(added_arr);
+ answers->correct_removed_v = string_array_to_vector(removed_arr);
+ answers->correct_unchanged_v = string_array_to_vector(unchanged_arr);
+ answers->correct_modified_v = string_array_to_vector(modified_arr);
+ return answers;
+}
+
+component_funcs_t *init_test_funcs(poldiff_get_diff_vector get_diff_vector, poldiff_get_name get_name, poldiff_get_form get_form,
+ poldiff_get_added get_added, poldiff_get_removed get_removed)
+{
+ component_funcs_t *funcs = (component_funcs_t *) malloc(sizeof(component_funcs_t));
+ funcs->get_diff_vector = get_diff_vector;
+ funcs->get_name = get_name;
+ funcs->get_form = get_form;
+ funcs->get_added = get_added;
+ funcs->get_removed = get_removed;
+ return funcs;
+}
+
+poldiff_t *init_poldiff(char *orig_base_path, char *mod_base_path)
+{
+ poldiff_t *return_diff = NULL;
+ uint32_t flags = POLDIFF_DIFF_ALL;
+ apol_policy_path_t *mod_pol_path = NULL;
+ apol_policy_path_t *orig_pol_path = NULL;
+
+ orig_pol_path = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, orig_base_path, NULL);
+ if (!orig_pol_path) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+
+ mod_pol_path = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, mod_base_path, NULL);
+ if (!mod_pol_path) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+
+ orig_policy = apol_policy_create_from_policy_path(orig_pol_path, 0, NULL, NULL);
+ if (!orig_policy) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+
+ mod_policy = apol_policy_create_from_policy_path(mod_pol_path, 0, NULL, NULL);
+ if (!mod_policy) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+
+ if (!(return_diff = poldiff_create(orig_policy, mod_policy, NULL, NULL))) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+ if (poldiff_run(return_diff, flags)) {
+ goto err;
+ }
+ apol_policy_path_destroy(&orig_pol_path);
+ apol_policy_path_destroy(&mod_pol_path);
+ return return_diff;
+ err:
+ apol_policy_destroy(&orig_policy);
+ apol_policy_destroy(&mod_policy);
+ apol_policy_path_destroy(&orig_pol_path);
+ apol_policy_path_destroy(&mod_pol_path);
+ poldiff_destroy(&return_diff);
+ return NULL;
+}
+
+void cleanup_test(poldiff_test_answers_t * answers)
+{
+ if (answers != NULL) {
+ apol_vector_destroy(&answers->correct_added_v);
+ apol_vector_destroy(&answers->correct_unchanged_v);
+ apol_vector_destroy(&answers->correct_removed_v);
+ apol_vector_destroy(&answers->correct_modified_v);
+ free(answers);
+ }
+}
+
+int poldiff_cleanup()
+{
+ poldiff_destroy(&diff);
+ return 0;
+}
+
+int main()
+{
+ if (CU_initialize_registry() != CUE_SUCCESS) {
+ return CU_get_error();
+ }
+
+ CU_TestInfo components_tests_arr[] = {
+ {"Attributes", components_attributes_tests}
+ ,
+ {"Classes", components_class_tests}
+ ,
+ {"Commons", components_commons_tests}
+ ,
+ {"Roles", components_roles_tests}
+ ,
+ {"Users", components_users_tests}
+ ,
+ {"Bools", components_bools_tests}
+ ,
+ {"Types", components_types_tests}
+ ,
+ CU_TEST_INFO_NULL
+ };
+
+ CU_TestInfo rules_tests_arr[] = {
+ {"AV Rules", rules_avrules_tests}
+ ,
+ {"TE Rules", rules_terules_tests}
+ ,
+ {"Role Allow Rules", rules_roleallow_tests}
+ ,
+ {"Role Transition Rules", rules_roletrans_tests}
+ ,
+ CU_TEST_INFO_NULL
+ };
+
+ CU_TestInfo mls_tests_arr[] = {
+ {"Categories", mls_category_tests}
+ ,
+ {"Levels", mls_level_tests}
+ ,
+ {"Range Transitions", mls_rangetrans_tests}
+ ,
+ {"Users (MLS)", mls_user_tests}
+ ,
+ CU_TEST_INFO_NULL
+ };
+ CU_TestInfo nomls_tests_arr[] = {
+ {"Changed & Unchanged Users", nomls_tests}
+ ,
+ CU_TEST_INFO_NULL
+ };
+
+ CU_SuiteInfo suites[] = {
+ {"Components", components_test_init, poldiff_cleanup, components_tests_arr}
+ ,
+ {"Rules", rules_test_init, poldiff_cleanup, rules_tests_arr}
+ ,
+ {"MLS", mls_test_init, poldiff_cleanup, mls_tests_arr}
+ ,
+ {"Non-MLS vs. MLS Users", nomls_test_init, poldiff_cleanup, nomls_tests_arr}
+ ,
+ 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/libpoldiff/tests/libpoldiff-tests.h b/libpoldiff/tests/libpoldiff-tests.h
new file mode 100644
index 0000000..d34c847
--- /dev/null
+++ b/libpoldiff/tests/libpoldiff-tests.h
@@ -0,0 +1,85 @@
+/**
+ * @file
+ *
+ * Header for for CUnit testing framework of libpoldiff's correctness.
+ *
+ * @author Paul Rosenfeld prosenfeld@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 LIBPOLDIFF_TESTS
+#define LIBPOLDIFF_TESTS
+
+#include <poldiff/poldiff.h>
+#include <apol/vector.h>
+
+typedef const apol_vector_t *(*poldiff_get_diff_vector) (const poldiff_t *);
+typedef const char *(*poldiff_get_name) (const void *);
+typedef poldiff_form_e(*poldiff_get_form) (const void *);
+typedef const apol_vector_t *(*poldiff_get_added) (const void *);
+typedef const apol_vector_t *(*poldiff_get_removed) (const void *);
+
+typedef struct _test_answers
+{
+ apol_vector_t *correct_added_v;
+ apol_vector_t *correct_removed_v;
+ apol_vector_t *correct_unchanged_v;
+ apol_vector_t *correct_modified_v;
+} poldiff_test_answers_t;
+
+typedef struct _component_funcs
+{
+ poldiff_get_diff_vector get_diff_vector;
+ poldiff_get_name get_name;
+ poldiff_get_form get_form;
+ poldiff_get_added get_added;
+ poldiff_get_removed get_removed;
+} component_funcs_t;
+
+typedef enum _test_numbers
+{
+ COMPONENT = 0, RULES_AVRULE, RULES_TERULE, RULES_ROLEALLOW, RULES_ROLETRANS,
+ MLS_CATEGORY, MLS_LEVEL, MLS_RANGETRANS, MLS_USER
+} test_numbers_e;
+
+poldiff_t *init_poldiff(char *orig_base_path, char *mod_base_path);
+component_funcs_t *init_test_funcs(poldiff_get_diff_vector, poldiff_get_name, poldiff_get_form, poldiff_get_added,
+ poldiff_get_removed);
+void run_test(component_funcs_t *, poldiff_test_answers_t *, test_numbers_e);
+
+apol_vector_t *string_array_to_vector(char *[]);
+void cleanup_test(poldiff_test_answers_t *);
+char *vector_to_string(const apol_vector_t *, const char *, const char *);
+
+int compare_str(const void *s1, const void *s2, void *debug);
+poldiff_test_answers_t *init_answer_vectors(char *[], char *[], char *[], char *[]);
+void print_test_failure(apol_vector_t *, apol_vector_t *, size_t, const char *);
+
+apol_vector_t *shallow_copy_str_vec_and_sort(const apol_vector_t * v);
+
+poldiff_t *diff;
+
+apol_policy_t *orig_policy;
+apol_policy_t *mod_policy;
+
+apol_vector_t *added_v;
+apol_vector_t *removed_v;
+apol_vector_t *modified_v;
+apol_vector_t *modified_name_only_v;
+
+#endif
diff --git a/libpoldiff/tests/mls-tests.c b/libpoldiff/tests/mls-tests.c
new file mode 100644
index 0000000..ad03feb
--- /dev/null
+++ b/libpoldiff/tests/mls-tests.c
@@ -0,0 +1,635 @@
+/**
+ * @file
+ *
+ * Test the libpoldiff's correctness for MLS.
+ *
+ * @author Paul Rosenfeld prosenfeld@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 "libpoldiff-tests.h"
+#include "mls-tests.h"
+#include "policy-defs.h"
+#include <CUnit/Basic.h>
+#include <CUnit/TestDB.h>
+
+#include <poldiff/poldiff.h>
+#include <apol/policy.h>
+#include <apol/vector.h>
+#include <apol/util.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *unchanged_users_mls[] = {
+ /* 13.0 */
+ "placeholder_u",
+ "reyna_u",
+ NULL
+};
+
+/* these aren't real tests, but the arrays must be declared anyways */
+char *added_users_mls[] = { NULL };
+char *removed_users_mls[] = { NULL };
+
+/* These strings are always in the same order: added, removed, modified
+ *
+ * Modified User fields are always in this order:
+ * d[...] represents a change in the default level
+ * range[...]
+ * roles[...]
+ */
+char *modified_users_mls[] = {
+ /* 13.3.03 */
+ "su_u: d[+s2 -s1]",
+ /* 13.3.04 */
+ "cyn_u: d[s1 +c2] range[*{s1:c1 c2 +c3}]",
+ /* 13.3.05 */
+ "devona_u: d[s1 -c1]",
+ /* 13.3.06 */
+ "danika_u: d[s1 +c3 -c1] range[*{s1:c1 c2 +c3}]",
+ /* 13.3.07 */
+ "mehnlo_u: range[+{s4:c4}]",
+ /* 13.3.08 */
+ "meloni_u: range[-{s4:c4 c5}]",
+ /* 13.3.09 */
+ "eve_u: range[+{s6} -{s0}]",
+ /* 13.3.10 */
+ "nika_u: range[*{s1:c1 c2 +c3} *{s2:c1 c2 c3 +c4}]",
+ /* 13.3.11 */
+ "koss_u: range[*{s3:c4 -c5} *{s4:c4 -c5} *{s5:c4 -c5}]",
+ /* 13.3.12 */
+ "kihm_u: range[+{s6:c1 c2 c3 c4 c5 c6} -{s0:c0 c1 c2} *{s1:c1 c2 +c3} *{s2:c1 c2 c3 c4 c5 -c0} *{s3:c1 c4 c5 +c6} *{s5:c1 c2 c3 c4 c5 +c6 -c0}]",
+ /* 13.3.13 */
+ "aidan_u: range[*{s1:c1 +c3 -c2} *{s2:c1 c3 -c2}]",
+ /* 13.3.14 */
+ "timera_u: d[+s2 -s1] roles[-admin_r]",
+ /* 13.3.15 */
+ "sheena_u: range[+{s4:c5} *{s2:+c5} *{s3:+c5}] roles[+user_r]",
+ /* 13.3.16 */
+ "chiyo_u: d[+s2 -s1] range[+{s4:c5} *{s2:+c5} *{s3:+c5}]",
+ /* 13.3.17 -- separate test -- see nomls-tests.c */
+ /* 13.3.18 -- separate test -- see nomls-tests.c */
+
+ /* 13.3.19 */
+ "jamei_u: d[+s2 -s1] range[+{s4:c5} *{s2:+c5} *{s3:+c5}] roles[-aquarium_r]",
+ NULL
+};
+
+char *unchanged_rangetrans[] = {
+/* 07.0 */
+ "range_transition placeholder_t oak_t : file s2",
+ NULL
+};
+
+char *added_rangetrans[] = {
+/* 07.1 */
+ "range_transition bear_t stone_t : gc s1",
+ "range_transition log_t bear_t : ipc s2",
+ "range_transition log_t file_t : fd s1",
+ "range_transition rock_t stone_t : dir s3",
+ NULL
+};
+char *removed_rangetrans[] = {
+
+/* 07.2 */
+ "range_transition potato_t daikon_t : dir s0:c2",
+ "range_transition rock_t stone_t : file s3",
+ "range_transition bear_t file_t : msg s1 - s5",
+ "range_transition bear_t log_t : msg s1 - s5",
+ "range_transition trout_t bear_t : pax s1",
+ NULL
+};
+
+/* m{...} represents a change in the minimum set of the transition and is always first,
+ * the rest of string is in the same order: added, removed, modified*/
+char *modified_rangetrans[] = {
+/* 07.3.0 */
+ "range_transition file_t system_t : process +{s2:c1} ",
+/* 07.3.1 */
+ "range_transition tiger_t trout_t : node m{+c1 +c2} -{s0:c1 c2}",
+/* 07.3.2 */
+ "range_transition glass_t log_t : netif +{s6:c1 c2 c3 c4 c5} -{s0:c1 c2} *{s1:c1 c2 +c3}",
+/* 07.3.3 */
+ "range_transition pine_t holly_t : lnk_file m{+c5} *{s3:c4 +c5}",
+/* 07.3.4 */
+ "range_transition rock_t finch_t : chr_file m{-c5} *{s3:c4 -c5}",
+/* 07.3.5 */
+ "range_transition trout_t dirt_t : blk_file m{-c0} *{s2:c1 c2 c3 c4 c5 -c0} *{s3:c1 c4 c5 +c6} *{s5:c1 c2 c3 c4 c5 +c6 -c0}",
+/* 07.3.6 */
+ "range_transition tiger_t stone_t : sock_file m{+c3} *{s1:c1 c2 +c3}",
+/* 07.3.7 */
+ "range_transition firefly_t log_t : fd m{+c5}",
+/* 07.3.8 */
+ "range_transition file_t trout_t : process m{-c2} *{s1:c1 c2 +c3}",
+/* 07.3.9 */
+ "range_transition pine_t oak_t : lnk_file m{+c5 -c0} *{s2:c1 c2 c3 c4 c5 -c0} *{s5:c1 c2 c3 c4 c5 -c0}",
+ NULL
+};
+char *added_rangetrans_type[] = {
+/* 07.4.0 */
+ "range_transition pipe_t rock_t : file s3",
+/* 07.4.1 */
+ "range_transition glass_t pipe_t : process s1",
+/* 07.4.2 */
+ "range_transition hippo_t file_t : msg s1 - s5",
+ "range_transition hippo_t log_t : msg s1 - s5",
+ "range_transition pipe_t oak_t : fifo_file s2 - s3:c5",
+/* 07.4.3 */
+ "range_transition lion_t pipe_t : msg s1 - s5",
+ "range_transition pine_t pipe_t : sem s1 - s4:c4.c5",
+ "range_transition tiger_t pipe_t : msg s1 - s5",
+/* 07.4.4 */
+ "range_transition pipe_t acorn_t : file s2",
+/* 07.4.5 */
+ "range_transition hippo_t pipe_t : msg s1 - s5",
+/* needs to be added */
+ "range_transition trout_t hippo_t : pax s1",
+ NULL
+};
+char *removed_rangetrans_type[] = {
+/* 07.5.0 */
+ "range_transition koala_t stone_t : gc s1",
+/* 07.5.1 */
+ "range_transition log_t koala_t : ipc s2",
+/* 07.5.2 */
+ "range_transition bass_t bear_t : pax s1",
+ "range_transition bass_t lion_t : pax s1",
+ "range_transition bass_t log_t : dir s3:c1",
+ "range_transition bass_t tiger_t : pax s1",
+/* 07.5.3 */
+ "range_transition firefly_t bass_t : passwd s2:c1.c5 - s5:c1.c5",
+/* "range_transition trout_t bear_t : pax s1", this rule is "simply removed" so its in the
+ normal removed array */
+/* 07.5.4 */
+ "range_transition bass_t koala_t : shm s0",
+/* 07.5.5 is a duplicate of the first rule in 07.5.2 */
+
+ NULL
+};
+
+char *unchanged_levels[] = {
+/* 06.0 */
+ "s4",
+ NULL
+};
+char *added_levels[] = {
+/* 06.1 */
+ "s6",
+ NULL
+};
+char *removed_levels[] = {
+/* 06.2 */
+ "s0",
+ NULL
+};
+char *modified_levels[] = {
+/* 06.3.0 */
+ "s3 +c6",
+/* 06.3.1 */
+ "s2 -c0",
+/* 06.3.2 */
+ "s5 +c6 -c0",
+/* 06.3.3 */
+ "s1 +c3",
+ NULL
+};
+
+char *unchanged_categories[] = {
+ /* 03.0 */
+ "c1", "c2", "c3", "c4", "c5",
+ NULL
+};
+char *added_categories[] = {
+ /* 03.1 */
+ "c6",
+ NULL
+};
+char *removed_categories[] = {
+ /* 03.2 */
+ "c0",
+ NULL
+};
+
+char *modified_categories[] = { NULL };
+
+int mls_test_init()
+{
+ if (!(diff = init_poldiff(MLS_ORIG_POLICY, MLS_MOD_POLICY))) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void build_category_vecs()
+{
+ char *str = NULL;
+ size_t i;
+ const void *item = NULL;
+ const apol_vector_t *v = NULL;
+ v = poldiff_get_cat_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); ++i) {
+ item = apol_vector_get_element(v, i);
+ const char *name = poldiff_cat_get_name(item);
+ str = strdup(name);
+ poldiff_form_e form = poldiff_cat_get_form(item);
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ default:
+ // can never get here
+ assert(0);
+ }
+ str = NULL;
+ }
+}
+
+char *level_to_string(const void *arg, poldiff_form_e form, int show_changes)
+{
+ poldiff_level_t *level = (poldiff_level_t *) arg;
+ char *str = NULL, *cat = NULL;
+ size_t i, str_len = 0;
+ const char *name = poldiff_level_get_name(level);
+ if (name) {
+ apol_str_appendf(&str, &str_len, "%s", name);
+ if (show_changes) {
+ if (form == POLDIFF_FORM_MODIFIED) {
+ const apol_vector_t *added_cats = poldiff_level_get_added_cats(level);
+ for (i = 0; i < apol_vector_get_size(added_cats); ++i) {
+ cat = apol_vector_get_element(added_cats, i);
+ apol_str_appendf(&str, &str_len, " +%s", cat);
+ }
+ const apol_vector_t *removed_cats = poldiff_level_get_removed_cats(level);
+ for (i = 0; i < apol_vector_get_size(removed_cats); ++i) {
+ cat = apol_vector_get_element(removed_cats, i);
+ apol_str_appendf(&str, &str_len, " -%s", cat);
+ }
+ }
+ }
+ }
+ if (str)
+ apol_str_trim(str);
+ return str;
+}
+
+char *modified_mls_range_to_string(const poldiff_range_t * range)
+{
+ char *str = NULL;
+ apol_vector_t *levels = NULL;
+ if (!(levels = poldiff_range_get_levels(range)))
+ goto err;
+ size_t i, str_len = 0;
+ char *min_set_str = NULL;
+ size_t min_set_str_len = 0;
+
+ apol_vector_t *min_set_added = poldiff_range_get_min_added_cats(range);
+ apol_vector_t *min_set_removed = poldiff_range_get_min_removed_cats(range);
+ size_t num_min_added = apol_vector_get_size(min_set_added);
+ size_t num_min_removed = apol_vector_get_size(min_set_removed);
+ if (min_set_added && num_min_added > 0) {
+ char *min_set_added_str = vector_to_string(min_set_added, "", " +");
+ apol_str_appendf(&min_set_str, &min_set_str_len, "%s", min_set_added_str);
+ free(min_set_added_str);
+ }
+ if (min_set_removed && num_min_removed > 0) {
+ char *min_set_removed_str = vector_to_string(min_set_removed, "", " -");
+ apol_str_appendf(&min_set_str, &min_set_str_len, "%s%s", num_min_added > 0 ? " " : "", min_set_removed_str);
+ free(min_set_removed_str);
+ }
+ if (num_min_added || num_min_removed) {
+ char *tmp = strdup(min_set_str);
+ free(min_set_str);
+ min_set_str = NULL;
+ min_set_str_len = 0;
+ apol_str_appendf(&min_set_str, &min_set_str_len, "m{%s} ", tmp);
+ free(tmp);
+ }
+ if (min_set_str) {
+ apol_str_appendf(&str, &str_len, "%s", min_set_str);
+ free(min_set_str);
+ }
+
+ for (i = 0; i < apol_vector_get_size(levels); ++i) {
+ poldiff_level_t *level = apol_vector_get_element(levels, i);
+ poldiff_form_e form = poldiff_level_get_form(level);
+ const char *level_str = poldiff_level_get_name(level);
+ char *sep = NULL, *add_sep = " +", *remove_sep = " -";
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ sep = "+";
+ add_sep = " ";
+ break;
+ case POLDIFF_FORM_REMOVED:
+ sep = "-";
+ remove_sep = " ";
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ sep = "*";
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ const apol_vector_t *unmod_cats = poldiff_level_get_unmodified_cats(level);
+ const apol_vector_t *added_cats = poldiff_level_get_added_cats(level);
+ const apol_vector_t *removed_cats = poldiff_level_get_removed_cats(level);
+ size_t num_unmod_cats = apol_vector_get_size(unmod_cats);
+ size_t num_added_cats = apol_vector_get_size(added_cats);
+ size_t num_removed_cats = apol_vector_get_size(removed_cats);
+ size_t num_cats = num_unmod_cats + num_added_cats + num_removed_cats;
+ char *unmod_cats_str = vector_to_string(unmod_cats, "", " ");
+ char *added_cats_str = vector_to_string(added_cats, num_unmod_cats > 0 ? " " : "", add_sep);
+ char *removed_cats_str = vector_to_string(removed_cats, num_added_cats > 0 ||
+ num_unmod_cats > 0 ? " " : "", remove_sep);
+ apol_str_appendf(&str, &str_len, "%s{%s%s%s%s%s} ", sep, level_str, num_cats > 0 ? ":" : "", unmod_cats_str,
+ added_cats_str, removed_cats_str);
+ free(unmod_cats_str);
+ free(added_cats_str);
+ free(removed_cats_str);
+ }
+ apol_str_trim(str);
+ return str;
+ err:
+ return NULL;
+}
+
+char *rangetrans_to_string(const void *arg, poldiff_form_e form, int show_changes)
+{
+ char *str = NULL;
+ size_t str_len = 0;
+ poldiff_range_trans_t *rt = (poldiff_range_trans_t *) arg;
+ const poldiff_range_t *range = poldiff_range_trans_get_range(rt);
+ const apol_mls_range_t *mod_range = poldiff_range_get_modified_range(range);
+ const apol_mls_range_t *orig_range = poldiff_range_get_original_range(range);
+ char *range_str = NULL;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ range_str = apol_mls_range_render(mod_policy, mod_range);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ range_str = apol_mls_range_render(orig_policy, orig_range);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ range_str = modified_mls_range_to_string(range);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ const char *source_type = poldiff_range_trans_get_source_type(rt);
+ const char *target_type = poldiff_range_trans_get_target_type(rt);
+ const char *target_class = poldiff_range_trans_get_target_class(rt);
+ if (show_changes) {
+ apol_str_appendf(&str, &str_len, "range_transition %s %s : %s %s", source_type, target_type, target_class,
+ range_str);
+ } else {
+ apol_str_appendf(&str, &str_len, "range_transition %s %s : %s", source_type, target_type, target_class);
+ }
+ free(range_str);
+ return str;
+}
+
+void build_rangetrans_vecs()
+{
+ apol_vector_t *added_rangetrans_type_v = apol_vector_create(free);
+ apol_vector_t *removed_rangetrans_type_v = apol_vector_create(free);
+ apol_vector_t *correct_added_rangetrans_type_v = string_array_to_vector(added_rangetrans_type);
+ apol_vector_t *correct_removed_rangetrans_type_v = string_array_to_vector(removed_rangetrans_type);
+
+ char *str = NULL, *name_only = NULL;
+ size_t i;
+ const void *item = NULL;
+ const apol_vector_t *v = NULL;
+ v = poldiff_get_range_trans_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); ++i) {
+ item = apol_vector_get_element(v, i);
+ poldiff_form_e form = poldiff_range_trans_get_form(item);
+ str = rangetrans_to_string(item, form, 1);
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_ADD_TYPE:
+ apol_vector_append(added_rangetrans_type_v, str);
+ break;
+ case POLDIFF_FORM_REMOVE_TYPE:
+ apol_vector_append(removed_rangetrans_type_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ name_only = rangetrans_to_string(item, form, 0);
+ apol_vector_append(modified_name_only_v, name_only);
+ apol_vector_append(modified_v, str);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ }
+ int test_result;
+ size_t first_diff = 0;
+ apol_vector_sort(added_rangetrans_type_v, compare_str, NULL);
+ apol_vector_sort(correct_added_rangetrans_type_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(added_rangetrans_type_v, correct_added_rangetrans_type_v, compare_str, NULL,
+ &first_diff));
+ if (test_result) {
+ print_test_failure(added_rangetrans_type_v, correct_added_rangetrans_type_v, first_diff, "Added Due to Types");
+ }
+ apol_vector_sort(removed_rangetrans_type_v, compare_str, NULL);
+ apol_vector_sort(correct_removed_rangetrans_type_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(removed_rangetrans_type_v, correct_removed_rangetrans_type_v, compare_str, NULL,
+ &first_diff));
+ if (test_result) {
+ print_test_failure(removed_rangetrans_type_v, correct_removed_rangetrans_type_v, first_diff,
+ "Removed Due to Types");
+ }
+ apol_vector_destroy(&added_rangetrans_type_v);
+ apol_vector_destroy(&correct_added_rangetrans_type_v);
+ apol_vector_destroy(&removed_rangetrans_type_v);
+ apol_vector_destroy(&correct_removed_rangetrans_type_v);
+
+}
+
+void build_level_vecs()
+{
+ char *str = NULL, *name_only = NULL;
+ size_t i;
+ const void *item = NULL;
+ const apol_vector_t *v = NULL;
+ v = poldiff_get_level_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); ++i) {
+ item = apol_vector_get_element(v, i);
+ poldiff_form_e form = poldiff_cat_get_form(item);
+ str = level_to_string(item, form, 1);
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ name_only = level_to_string(item, form, 0);
+ apol_vector_append(modified_name_only_v, name_only);
+ apol_vector_append(modified_v, str);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ }
+}
+
+char *mls_user_to_string(const void *arg, poldiff_form_e form, int show_changes)
+{
+ poldiff_user_t *u = (poldiff_user_t *) arg;
+ char *str = NULL, *dlevel_str = NULL, *range_str = NULL, *roles_str = NULL;
+ size_t str_len = 0, dlevel_str_len = 0, range_str_len = 0, roles_str_len = 0;
+ const poldiff_range_t *range = poldiff_user_get_range(u);
+ const poldiff_level_t *orig_level = poldiff_user_get_original_dfltlevel(u);
+ const poldiff_level_t *mod_level = poldiff_user_get_modified_dfltlevel(u);
+ poldiff_form_e orig_form = poldiff_level_get_form(orig_level);
+ poldiff_form_e mod_form = poldiff_level_get_form(mod_level);
+ char *orig_level_str = level_to_string(orig_level, orig_form, 1);
+ char *mod_level_str = level_to_string(mod_level, mod_form, 1);
+ //change of default sensitivity
+ if (mod_level_str && orig_level_str) {
+ apol_str_appendf(&dlevel_str, &dlevel_str_len, "d[+%s -%s] ", mod_level_str, orig_level_str);
+ }
+ //change of default category within a sensitivity
+ else if (!mod_level_str && orig_level_str) {
+ apol_str_appendf(&dlevel_str, &dlevel_str_len, "d[%s] ", orig_level_str);
+ } else if (!orig_level_str && mod_level_str) {
+ //this should never happen
+ CU_ASSERT_FALSE(1);
+ }
+ if ((range_str = modified_mls_range_to_string(range)) != NULL) {
+ char *tmp = strdup(range_str);
+ free(range_str);
+ range_str = NULL;
+ range_str_len = 0;
+ apol_str_appendf(&range_str, &range_str_len, "range[%s] ", tmp);
+ free(tmp);
+ }
+ char *added_roles_str = vector_to_string(poldiff_user_get_added_roles(u), "", " +");
+ char *removed_roles_str = vector_to_string(poldiff_user_get_removed_roles(u), "", " -");
+ if (strlen(added_roles_str) > 0 || strlen(removed_roles_str) > 0) {
+ apol_str_appendf(&roles_str, &roles_str_len, "roles[%s%s] ", added_roles_str ? added_roles_str : "",
+ removed_roles_str ? removed_roles_str : "");
+ }
+ const char *user_name = poldiff_user_get_name(u);
+ if (show_changes) {
+ apol_str_appendf(&str, &str_len, "%s: %s%s%s", user_name, dlevel_str ? dlevel_str : "", range_str ? range_str : "",
+ roles_str ? roles_str : "");
+ } else {
+ apol_str_appendf(&str, &str_len, "%s", user_name);
+ }
+
+ free(range_str);
+ free(roles_str);
+ free(dlevel_str);
+ free(mod_level_str);
+ free(orig_level_str);
+ free(added_roles_str);
+ free(removed_roles_str);
+ return str;
+}
+
+void build_user_vecs()
+{
+ char *str = NULL, *name_only;
+ size_t i;
+ const void *item = NULL;
+ const apol_vector_t *v = NULL;
+ v = poldiff_get_user_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); ++i) {
+ item = apol_vector_get_element(v, i);
+ poldiff_form_e form = poldiff_cat_get_form(item);
+ str = mls_user_to_string(item, form, 1);
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ name_only = mls_user_to_string(item, form, 0);
+ apol_vector_append(modified_name_only_v, name_only);
+ apol_vector_append(modified_v, str);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ str = NULL;
+ }
+}
+
+void mls_category_tests()
+{
+ test_numbers_e test_num = MLS_CATEGORY;
+ poldiff_test_answers_t *answers =
+ init_answer_vectors(added_categories, removed_categories, unchanged_categories, modified_categories);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
+
+void mls_rangetrans_tests()
+{
+ test_numbers_e test_num = MLS_RANGETRANS;
+ poldiff_test_answers_t *answers =
+ init_answer_vectors(added_rangetrans, removed_rangetrans, unchanged_rangetrans, modified_rangetrans);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
+
+void mls_level_tests()
+{
+ test_numbers_e test_num = MLS_LEVEL;
+ poldiff_test_answers_t *answers = init_answer_vectors(added_levels, removed_levels, unchanged_levels, modified_levels);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
+
+void mls_user_tests()
+{
+ test_numbers_e test_num = MLS_USER;
+ poldiff_test_answers_t *answers =
+ init_answer_vectors(added_users_mls, removed_users_mls, unchanged_users_mls, modified_users_mls);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
diff --git a/libpoldiff/tests/mls-tests.h b/libpoldiff/tests/mls-tests.h
new file mode 100644
index 0000000..8922ebb
--- /dev/null
+++ b/libpoldiff/tests/mls-tests.h
@@ -0,0 +1,39 @@
+/**
+ * @file
+ *
+ * Header file for libpoldiff's correctness of MLS.
+ *
+ * @author Paul Rosenfeld prosenfeld@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 MLS_TEST
+#define MLS_TEST
+int mls_test_init();
+int mls_test_cleanup();
+
+void mls_category_tests();
+void mls_user_tests();
+void mls_rangetrans_tests();
+void mls_level_tests();
+void build_category_vecs();
+void build_rangetrans_vecs();
+void build_level_vecs();
+void build_user_vecs();
+
+#endif
diff --git a/libpoldiff/tests/nomls-tests.c b/libpoldiff/tests/nomls-tests.c
new file mode 100644
index 0000000..da7dad2
--- /dev/null
+++ b/libpoldiff/tests/nomls-tests.c
@@ -0,0 +1,139 @@
+/**
+ * @file
+ *
+ * Test the libpoldiff's correctness for MLS versus non-MLS policies.
+ *
+ * @author Paul Rosenfeld prosenfeld@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 "libpoldiff-tests.h"
+#include "nomls-tests.h"
+#include "policy-defs.h"
+#include <CUnit/Basic.h>
+#include <CUnit/TestDB.h>
+
+#include <poldiff/poldiff.h>
+#include <apol/util.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *nomls_unchanged_users[] = {
+/* 13.3.17 */
+ "placeholder_u",
+ "su_u",
+ "cyn_u",
+ "devona_u",
+ "danika_u",
+ "mehnlo_u",
+ "meloni_u",
+ "eve_u",
+ "nika_u",
+ "koss_u",
+ "kihm_u",
+ "aidan_u",
+ "chiyo_u",
+ "reyna_u",
+ NULL
+};
+apol_vector_t *unchanged_users_v;
+apol_vector_t *changed_users_v;
+
+char *nomls_changed_users[] = {
+/* 13.3.18 */
+ "timera_u -admin_r",
+ "sheena_u +user_r",
+ "jamei_u -aquarium_r",
+ NULL
+};
+
+int nomls_test_init()
+{
+ if (!(diff = init_poldiff(NOMLS_ORIG_POLICY, NOMLS_MOD_POLICY))) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void build_nomls_vecs()
+{
+ const void *item;
+ const apol_vector_t *v = NULL;
+ size_t i, str_len = 0;
+ char *str = NULL;
+ v = poldiff_get_user_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); ++i) {
+ item = apol_vector_get_element(v, i);
+ poldiff_user_t *u = (poldiff_user_t *) item;
+ const char *name = poldiff_user_get_name(u);
+ const apol_vector_t *added_roles = poldiff_user_get_added_roles(u);
+ const apol_vector_t *removed_roles = poldiff_user_get_removed_roles(u);
+ if (apol_vector_get_size(added_roles) == 0 && apol_vector_get_size(removed_roles) == 0) {
+ apol_vector_append(unchanged_users_v, strdup(name));
+ } else {
+ char *added_roles_str = vector_to_string(added_roles, "", " +");
+ char *removed_roles_str = vector_to_string(removed_roles, "-", " ");
+ apol_str_appendf(&str, &str_len, "%s %s%s", name, added_roles_str, removed_roles_str);
+ free(added_roles_str);
+ free(removed_roles_str);
+ apol_str_trim(str);
+ apol_vector_append(changed_users_v, str);
+ str = NULL;
+ str_len = 0;
+ }
+ }
+}
+void nomls_tests()
+{
+ size_t first_diff = 0;
+ int test_result;
+ unchanged_users_v = apol_vector_create(free);
+ changed_users_v = apol_vector_create(free);
+
+ apol_vector_t *correct_unchanged_users_v = string_array_to_vector(nomls_unchanged_users);
+ apol_vector_t *correct_changed_users_v = string_array_to_vector(nomls_changed_users);
+
+ build_nomls_vecs();
+ apol_vector_sort(unchanged_users_v, compare_str, NULL);
+ apol_vector_sort(correct_unchanged_users_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(unchanged_users_v, correct_unchanged_users_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(unchanged_users_v, correct_unchanged_users_v, first_diff, "Unchanged MLS Users");
+ }
+ apol_vector_sort(changed_users_v, compare_str, NULL);
+ apol_vector_sort(correct_changed_users_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(changed_users_v, correct_changed_users_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(changed_users_v, correct_changed_users_v, first_diff, "Changed MLS Users");
+ }
+ apol_vector_destroy(&unchanged_users_v);
+ apol_vector_destroy(&changed_users_v);
+ apol_vector_destroy(&correct_unchanged_users_v);
+ apol_vector_destroy(&correct_changed_users_v);
+
+}
diff --git a/libpoldiff/tests/nomls-tests.h b/libpoldiff/tests/nomls-tests.h
new file mode 100644
index 0000000..3232fd0
--- /dev/null
+++ b/libpoldiff/tests/nomls-tests.h
@@ -0,0 +1,33 @@
+/**
+ * @file
+ *
+ * Header file for libpoldiff's correctness of MLS versus non-MLS policies.
+ *
+ * @author Paul Rosenfeld prosenfeld@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 NOMLS_TEST
+#define NOMLS_TEST
+int nomls_test_init();
+int nomls_test_cleanup();
+
+void nomls_tests();
+void nomls_unchanged_test();
+
+#endif
diff --git a/libpoldiff/tests/policy-defs.h b/libpoldiff/tests/policy-defs.h
new file mode 100644
index 0000000..37a925b
--- /dev/null
+++ b/libpoldiff/tests/policy-defs.h
@@ -0,0 +1,44 @@
+/**
+ * @file
+ *
+ * Header file defining location of test policies.
+ *
+ * @author Paul Rosenfeld prosenfeld@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_DEFS
+#define POLICY_DEFS
+
+#include <config.h>
+
+#define POLICY_ROOT TEST_POLICIES "/setools-3.2/sediff"
+
+#define COMPONENTS_ORIG_POLICY (POLICY_ROOT "/testing-component-orig.conf")
+#define COMPONENTS_MOD_POLICY (POLICY_ROOT "/testing-component-mod.conf")
+
+#define RULES_ORIG_POLICY (POLICY_ROOT "/testing-rules-orig.conf")
+#define RULES_MOD_POLICY (POLICY_ROOT "/testing-rules-mod.conf")
+
+#define MLS_ORIG_POLICY (POLICY_ROOT "/testing-mls-orig.conf")
+#define MLS_MOD_POLICY (POLICY_ROOT "/testing-mls-mod.conf")
+
+#define NOMLS_ORIG_POLICY (POLICY_ROOT "/testing-mls-orig.conf")
+#define NOMLS_MOD_POLICY (POLICY_ROOT "/testing-mls-mod-nomls.conf")
+
+#endif
diff --git a/libpoldiff/tests/rules-tests.c b/libpoldiff/tests/rules-tests.c
new file mode 100644
index 0000000..c7b7ef1
--- /dev/null
+++ b/libpoldiff/tests/rules-tests.c
@@ -0,0 +1,914 @@
+/**
+ * @file
+ *
+ * Test the libpoldiff's correctness for rules.
+ *
+ * @author Paul Rosenfeld prosenfeld@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 "libpoldiff-tests.h"
+#include "rules-tests.h"
+#include "policy-defs.h"
+#include <CUnit/Basic.h>
+#include <CUnit/TestDB.h>
+
+#include <poldiff/poldiff.h>
+#include <apol/policy.h>
+#include <apol/vector.h>
+#include <apol/util.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+static apol_vector_t *added_type_rules_v;
+static apol_vector_t *removed_type_rules_v;
+static apol_vector_t *correct_added_type_rules_v;
+static apol_vector_t *correct_removed_type_rules_v;
+
+char *unchanged_avrules[] = {
+/* 01.0 */
+ "allow placeholder_t placeholder_t : file read",
+ "auditallow potato_t pine_t : dir setattr",
+ NULL
+};
+char *added_avrules[] = {
+/* 01.1 */
+ "allow bear_t oak_t : fifo_file write",
+ "allow rock_t log_t : file getattr",
+ "allow tiger_t bear_t : file execute",
+ "auditallow system_t log_t : netif udp_recv",
+ "neverallow lion_t bear_t : file execute",
+ NULL
+};
+char *removed_avrules[] = {
+/* 01.2 */
+ "allow rock_t log_t : dir search",
+ "auditallow system_t log_t : node udp_recv",
+ "allow bear_t bear_t : dir search",
+ "allow bear_t birch_t : fd use",
+ "allow bear_t daikon_t : fd use",
+ "allow bear_t glass_t : file getattr",
+ "allow bear_t holly_t : fd use",
+ "allow bear_t oak_t : fd use",
+ "allow bear_t pine_t : fd use",
+ "allow bear_t potato_t : fd use",
+ NULL
+};
+
+char *modified_avrules[] = {
+/*01.3.0*/
+ "allow firefly_t file_t : file execute +lock",
+/*01.3.1*/
+ "dontaudit bass_t stone_t : dir read search -getattr",
+ "dontaudit trout_t stone_t : dir read search -getattr",
+/*01.3.2*/
+ "allow potato_t daikon_t : file getattr ioctl setattr +write -read",
+ NULL
+};
+
+char *added_type_avrules[] = {
+/* 01.4.00 */
+ "auditallow pipe_t bear_t : blk_file ioctl",
+/* 01.4.01 */
+ "auditallow dirt_t hippo_t : sock_file read",
+/* 01.4.02 */
+ "allow hippo_t birch_t : fd use",
+ "allow hippo_t daikon_t : fd use",
+ "allow hippo_t glass_t : file getattr",
+ "allow hippo_t holly_t : fd use",
+ "allow hippo_t oak_t : fd use",
+ "allow hippo_t pine_t : fd use",
+ "allow hippo_t potato_t : fd use",
+/* 01.4.03 */
+ "allow system_t pipe_t : file getattr ioctl read",
+ "neverallow bear_t pipe_t : process transition",
+ "neverallow lion_t pipe_t : process transition",
+ "neverallow tiger_t pipe_t : process transition",
+/* 01.4.04 */
+ "allow hippo_t pipe_t : lnk_file write",
+/* 01.4.05 */
+ "neverallow hippo_t pipe_t : process transition",
+/* 01.4.06 */
+ "allow hippo_t hippo_t : file getattr",
+/* 01.4.07 */
+ "allow hippo_t hippo_t : dir search",
+/* 01.4.08 */
+ "neverallow hippo_t finch_t : dir add_name",
+/*"neverallow pipe_t finch_t : dir add_name",*/
+ "neverallow pipe_t potato_t : lnk_file write",
+ "neverallow pipe_t system_t : lnk_file write",
+ "neverallow pipe_t bass_t : lnk_file write",
+ "neverallow pipe_t bear_t : lnk_file write",
+ "neverallow pipe_t birch_t : lnk_file write",
+ "neverallow pipe_t daikon_t : lnk_file write",
+ "neverallow pipe_t dirt_t : lnk_file write",
+ "neverallow pipe_t finch_t : lnk_file write",
+ "neverallow pipe_t firefly_t : lnk_file write",
+ "neverallow pipe_t glass_t : lnk_file write",
+ "neverallow pipe_t holly_t : lnk_file write",
+ "neverallow pipe_t lion_t : lnk_file write",
+ "neverallow pipe_t oak_t : lnk_file write",
+ "neverallow pipe_t pine_t : lnk_file write",
+ "neverallow pipe_t placeholder_t : lnk_file write",
+ "neverallow pipe_t rock_t : lnk_file write",
+ "neverallow pipe_t stone_t : lnk_file write",
+ "neverallow pipe_t tiger_t : lnk_file write",
+ "neverallow pipe_t trout_t : lnk_file write",
+/*01.4.09*/
+ "neverallow birch_t hippo_t : lnk_file write",
+ "neverallow daikon_t hippo_t : lnk_file write",
+ "neverallow dirt_t hippo_t : lnk_file write",
+ "neverallow file_t hippo_t : lnk_file write",
+ "neverallow glass_t hippo_t : lnk_file write",
+ "neverallow holly_t hippo_t : lnk_file write",
+ "neverallow lion_t pipe_t : file execute",
+ "neverallow log_t hippo_t : lnk_file write",
+ "neverallow oak_t hippo_t : lnk_file write",
+ "neverallow pine_t hippo_t : lnk_file write",
+ "neverallow potato_t hippo_t : lnk_file write",
+ "neverallow placeholder_t hippo_t : lnk_file write",
+ "neverallow rock_t hippo_t : lnk_file write",
+ "neverallow stone_t hippo_t : lnk_file write",
+ "neverallow system_t hippo_t : lnk_file write",
+/* 01.4.10 */
+ "neverallow pipe_t hippo_t : lnk_file write",
+/* 01.4.11 */
+ "neverallow hippo_t log_t : file execute",
+ "neverallow pipe_t log_t : file execute",
+/* 01.4.12 */
+ "neverallow placeholder_t hippo_t : fd use",
+ "neverallow placeholder_t pipe_t : fd use",
+/*********** NEED TO BE ADDED TO DOCUMENT *******/
+ "neverallow bass_t pipe_t : process transition",
+ "neverallow finch_t pipe_t : process transition",
+ "neverallow firefly_t pipe_t : process transition",
+ "neverallow hippo_t file_t : process transition",
+ "neverallow hippo_t log_t : process transition",
+ "neverallow trout_t pipe_t : process transition",
+ NULL
+};
+char *removed_type_avrules[] = {
+/* 01.5.00 */
+ "allow koala_t oak_t : fifo_file write",
+/* 01.5.01 */
+ "allow tiger_t koala_t : file execute",
+/* 01.5.02 */
+/*"allow bear_t glass_t : file getattr",
+BEAR_T IS NO LONGER MAMMAL, THIS RULES DOESN'T APPLY*/
+ "allow turnip_t dirt_t : dir search",
+ "neverallow koala_t file_t : process transition",
+ "neverallow koala_t log_t : process transition",
+/* 01.5.03 */
+ "allow bear_t turnip_t : fd use",
+ "allow lion_t turnip_t : fd use",
+ "allow stone_t turnip_t : blk_file write",
+ "allow tiger_t turnip_t : fd use",
+/* 01.5.04 */
+ "allow koala_t turnip_t : lnk_file read",
+/* 01.5.05
+"allow bear_t turnip_t : fd use",
+WRONG
+*/
+/* 01.5.06 */
+ "allow turnip_t turnip_t : fd use",
+/* 01.5.07 */
+/*"allow bear_t bear_t : dir search",
+BEAR_T IS NO LONGER MAMMAL, THIS RULE DOESNT APPLY*/
+/* 01.5.08 */
+ "neverallow koala_t finch_t : dir add_name",
+ "neverallow turnip_t finch_t : dir add_name",
+ "neverallow turnip_t potato_t : lnk_file write",
+ "neverallow turnip_t system_t : lnk_file write",
+ "neverallow turnip_t bass_t : lnk_file write",
+ "neverallow turnip_t bear_t : lnk_file write",
+ "neverallow turnip_t birch_t : lnk_file write",
+ "neverallow turnip_t daikon_t : lnk_file write",
+ "neverallow turnip_t dirt_t : lnk_file write",
+ "neverallow turnip_t finch_t : lnk_file write",
+ "neverallow turnip_t firefly_t : lnk_file write",
+ "neverallow turnip_t glass_t : lnk_file write",
+ "neverallow turnip_t holly_t : lnk_file write",
+ "neverallow turnip_t lion_t : lnk_file write",
+ "neverallow turnip_t oak_t : lnk_file write",
+ "neverallow turnip_t pine_t : lnk_file write",
+ "neverallow turnip_t placeholder_t : lnk_file write",
+ "neverallow turnip_t rock_t : lnk_file write",
+ "neverallow turnip_t stone_t : lnk_file write",
+ "neverallow turnip_t tiger_t : lnk_file write",
+ "neverallow turnip_t trout_t : lnk_file write",
+/* 01.5.09 */
+ "neverallow birch_t koala_t : lnk_file write",
+ "neverallow birch_t turnip_t : lnk_file write",
+ "neverallow daikon_t koala_t : lnk_file write",
+ "neverallow daikon_t turnip_t : lnk_file write",
+ "neverallow dirt_t koala_t : lnk_file write",
+ "neverallow dirt_t turnip_t : lnk_file write",
+ "neverallow file_t koala_t : lnk_file write",
+ "neverallow file_t turnip_t : lnk_file write",
+ "neverallow glass_t koala_t : lnk_file write",
+ "neverallow glass_t turnip_t : lnk_file write",
+ "neverallow holly_t koala_t : lnk_file write",
+ "neverallow holly_t turnip_t : lnk_file write",
+ "neverallow lion_t koala_t : file execute",
+ "neverallow log_t koala_t : lnk_file write",
+ "neverallow log_t turnip_t : lnk_file write",
+ "neverallow oak_t koala_t : lnk_file write",
+ "neverallow oak_t turnip_t : lnk_file write",
+ "neverallow placeholder_t koala_t : lnk_file write",
+ "neverallow placeholder_t turnip_t : lnk_file write",
+ "neverallow pine_t koala_t : lnk_file write",
+ "neverallow pine_t turnip_t : lnk_file write",
+ "neverallow potato_t koala_t : lnk_file write",
+ "neverallow potato_t turnip_t : lnk_file write",
+ "neverallow rock_t koala_t : lnk_file write",
+ "neverallow rock_t turnip_t : lnk_file write",
+ "neverallow stone_t koala_t : lnk_file write",
+ "neverallow stone_t turnip_t : lnk_file write",
+ "neverallow system_t koala_t : lnk_file write",
+ "neverallow system_t turnip_t : lnk_file write",
+/* 01.5.10 */
+ "neverallow turnip_t koala_t : lnk_file write",
+ "neverallow turnip_t turnip_t : lnk_file write",
+/* 01.5.11 */
+ "neverallow koala_t log_t : file execute",
+ "neverallow turnip_t log_t : file execute",
+/* 01.5.12 */
+ "neverallow placeholder_t koala_t : fd use",
+ "neverallow placeholder_t turnip_t : fd use",
+ NULL
+};
+
+char *unchanged_roleallowrules[] = {
+/* 09.0*/
+ "allow admin_r staff_r user_r",
+ "allow deity_r { admin_r aquarium_r garden_r guest_r intern_r lumberjack_r mammal_r placeholder_r staff_r user_r zoo_r }",
+ "allow mammal_r intern_r user_r",
+ "allow placeholder_r staff_r",
+ NULL
+};
+char *added_roleallowrules[] = {
+/* 09.1 */
+ "allow intern_r user_r",
+ NULL
+};
+char *removed_roleallowrules[] = {
+/* 09.2 */
+ "allow guest_r user_r",
+ NULL
+};
+char *modified_roleallowrules[] = {
+/* 09.3.0 */
+ "allow aquarium_r { guest_r staff_r +admin_r }",
+ "allow user_r { placeholder_r +guest_r }",
+/* 09.3.1 */
+ "allow garden_r { guest_r -user_r -zoo_r }",
+ "allow lumberjack_r { garden_r -staff_r }",
+ "allow zoo_r { aquarium_r garden_r mammal_r -admin_r }",
+/* 09.3.2 */
+ "allow staff_r { guest_r user_r +mammal_r -intern_r }",
+ NULL
+};
+
+char *unchanged_roletrans_rules[] = {
+/* 10.0*/
+ "role_transition garden_r birch_t lumberjack_r",
+ "role_transition garden_r oak_t lumberjack_r",
+ "role_transition garden_r pine_t lumberjack_r",
+ "role_transition staff_r holly_t garden_r",
+ NULL
+};
+char *added_roletrans_rules[] = {
+/* 10.1 */
+ "role_transition guest_r bear_t staff_r",
+ "role_transition intern_r file_t staff_r",
+ NULL
+};
+char *removed_roletrans_rules[] = {
+/* 10.2 */
+ "role_transition zoo_r bass_t aquarium_r",
+ "role_transition zoo_r bear_t mammal_r",
+ "role_transition zoo_r trout_t aquarium_r",
+ NULL
+};
+char *modified_roletrans_rules[] = {
+/* 10.3.0 */
+ "role_transition guest_r dirt_t { +admin_r -intern_r }",
+ NULL
+};
+char *added_roletrans_type[] = {
+/* 10.4.0 */
+ "role_transition guest_r pipe_t staff_r",
+/* 10.4.1 */
+ "role_transition admin_r pipe_t staff_r",
+ "role_transition staff_r hippo_t zoo_r",
+ "role_transition zoo_r hippo_t mammal_r",
+ NULL
+};
+
+char *removed_roletrans_type[] = {
+/* 10.5.0 */
+ "role_transition guest_r koala_t staff_r",
+/* 10.5.1 */
+ "role_transition staff_r koala_t zoo_r",
+ NULL
+};
+
+char *unchanged_terules[] = {
+/* 11.0 */
+ "type_transition system_t dirt_t : process daikon_t",
+ NULL
+};
+char *added_terules[] = {
+/* 11.1 */
+ "type_member log_t file_t : netif rock_t",
+ "type_transition holly_t bear_t : dir oak_t",
+ NULL
+};
+char *removed_terules[] = {
+/* 11.2 */
+ "type_transition potato_t pine_t : fd log_t",
+ "type_change file_t bear_t : passwd daikon_t",
+ "type_member log_t file_t : node rock_t",
+ "type_change log_t bear_t : passwd daikon_t",
+ NULL
+};
+char *added_type_terules[] = {
+/*11.4.0 */
+ "type_transition hippo_t log_t : file system_t",
+/*11.4.1 */
+ "type_transition bear_t pipe_t : chr_file birch_t",
+/*11.4.2 */
+ "type_transition hippo_t stone_t : netif potato_t",
+/*11.4.3 */
+ "type_change glass_t hippo_t : socket bass_t",
+/*11.4.4 */
+ "type_change hippo_t pipe_t : gc log_t",
+/*11.4.5 */
+ "type_change file_t hippo_t : passwd daikon_t",
+ "type_change log_t hippo_t : passwd daikon_t",
+ "type_change pipe_t hippo_t : passwd daikon_t",
+ "type_change pipe_t lion_t : passwd daikon_t",
+ "type_change pipe_t tiger_t : passwd daikon_t",
+ "type_member hippo_t birch_t : chr_file file_t",
+ "type_member hippo_t daikon_t : chr_file file_t",
+ "type_member hippo_t holly_t : chr_file file_t",
+ "type_member hippo_t oak_t : chr_file file_t",
+ "type_member hippo_t pine_t : chr_file file_t",
+ "type_member hippo_t potato_t : chr_file file_t",
+ NULL
+};
+char *removed_type_terules[] = {
+/* 11.5.0 */
+ "type_change turnip_t glass_t : dir stone_t",
+/* 11.5.1 */
+ "type_change tiger_t turnip_t : file file_t",
+/* 11.5.2 */
+ "type_member turnip_t dirt_t : dir glass_t",
+/* 11.5.3 */
+ "type_member firefly_t turnip_t : file pine_t",
+/* 11.5.4 */
+ "type_member turnip_t turnip_t : fd lion_t",
+/* 11.5.5 */
+ "type_member bass_t turnip_t : chr_file file_t",
+ "type_member bear_t turnip_t : chr_file file_t",
+ "type_member finch_t turnip_t : chr_file file_t",
+ "type_member firefly_t turnip_t : chr_file file_t",
+/* these rules are incorrect because there was no hippo_t in the original policy, so it cannot be removed
+"type_member hippo_t birch_t : chr_file file_t",
+"type_member hippo_t daikon_t : chr_file file_t",
+"type_member hippo_t holly_t : chr_file file_t",
+"type_member hippo_t oak_t : chr_file file_t",
+"type_member hippo_t pine_t : chr_file file_t",
+"type_member hippo_t potato_t : chr_file file_t",
+*/
+ "type_member koala_t birch_t : chr_file file_t",
+ "type_member koala_t daikon_t : chr_file file_t",
+ "type_member koala_t holly_t : chr_file file_t",
+ "type_member koala_t oak_t : chr_file file_t",
+ "type_member koala_t pine_t : chr_file file_t",
+ "type_member koala_t potato_t : chr_file file_t",
+ "type_member koala_t turnip_t : chr_file file_t",
+ "type_member lion_t turnip_t : chr_file file_t",
+ "type_member tiger_t turnip_t : chr_file file_t",
+ "type_member trout_t turnip_t : chr_file file_t",
+/* koala_t is now an alias of animal, thus this rule now applies:
+ type_change glass_t animal : socket bass_t;
+ */
+ "type_change glass_t koala_t : socket bass_t",
+/* also this rule applies:
+ type_transition animal stone_t : netif potato_t;
+*/
+ "type_transition koala_t stone_t : netif potato_t",
+ NULL
+};
+
+char *modified_terules[] = {
+ "type_transition lion_t tiger_t : file +bear_t -koala_t",
+ NULL
+};
+
+static char *get_rule_modification_str(const apol_vector_t * unmodified, const apol_vector_t * added, const apol_vector_t * removed,
+ poldiff_form_e form, int show_changes)
+{
+ char *perm_add_char = "+", *perm_remove_char = "-";
+ apol_vector_t *added_copy = shallow_copy_str_vec_and_sort(added);
+ apol_vector_t *removed_copy = shallow_copy_str_vec_and_sort(removed);
+ apol_vector_t *unmodified_copy = shallow_copy_str_vec_and_sort(unmodified);
+ int error = 0;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ perm_add_char = "";
+ break;
+ case POLDIFF_FORM_REMOVE_TYPE:
+ case POLDIFF_FORM_REMOVED:
+ perm_remove_char = "";
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ // do nothing
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ size_t i, str_len;
+ char *perm_name = NULL, *str = NULL;
+ for (i = 0; unmodified_copy != NULL && i < apol_vector_get_size(unmodified_copy); ++i) {
+ char *unmod_perm = apol_vector_get_element(unmodified_copy, i);
+ apol_str_appendf(&str, &str_len, " %s", unmod_perm);
+ }
+ if (show_changes) {
+ for (i = 0; added != NULL && i < apol_vector_get_size(added); i++) {
+ perm_name = (char *)apol_vector_get_element(added_copy, i);
+ if (apol_str_appendf(&str, &str_len, " %s%s", perm_add_char, perm_name) < 0) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (i = 0; removed != NULL && i < apol_vector_get_size(removed_copy); i++) {
+ perm_name = (char *)apol_vector_get_element(removed_copy, i);
+ if (apol_str_appendf(&str, &str_len, " %s%s", perm_remove_char, perm_name) < 0) {
+ error = errno;
+ goto err;
+ }
+ }
+ }
+ apol_vector_destroy(&added_copy);
+ apol_vector_destroy(&removed_copy);
+ apol_vector_destroy(&unmodified_copy);
+ return str;
+ err:
+ free(str);
+ return NULL;
+}
+
+static char *avrule_to_string(const void *arg, poldiff_form_e form, int show_changes)
+{
+ const poldiff_avrule_t *avr = (const poldiff_avrule_t *)arg;
+ char *str = NULL;
+ size_t str_len = 0;
+ uint32_t rule_type = poldiff_avrule_get_rule_type(avr);
+ const char *rule_type_str = apol_rule_type_to_str(rule_type);
+ const char *target_type = poldiff_avrule_get_target_type(avr);
+ const char *source_type = poldiff_avrule_get_source_type(avr);
+ const char *object_class = poldiff_avrule_get_object_class(avr);
+ apol_str_appendf(&str, &str_len, "%s %s %s : %s", rule_type_str, source_type, target_type, object_class);
+ if (show_changes) {
+ const apol_vector_t *unmodified_perms = poldiff_avrule_get_unmodified_perms(avr);
+ const apol_vector_t *removed_perms = poldiff_avrule_get_removed_perms(avr);
+ const apol_vector_t *added_perms = poldiff_avrule_get_added_perms(avr);
+ char *perm_str = get_rule_modification_str(unmodified_perms, added_perms, removed_perms, form, show_changes);
+ apol_str_appendf(&str, &str_len, "%s", perm_str);
+ free(perm_str);
+ }
+ return str;
+}
+
+static char *terule_to_string(const void *arg, poldiff_form_e form, int show_changes)
+{
+ poldiff_terule_t *ter = (poldiff_terule_t *) arg;
+ char *str = NULL;
+ size_t str_len = 0;
+ uint32_t rule_type = poldiff_terule_get_rule_type(ter);
+ const char *rule_type_str = apol_rule_type_to_str(rule_type);
+ const char *target_type = poldiff_terule_get_target_type(ter);
+ const char *source_type = poldiff_terule_get_source_type(ter);
+ const char *object_class = poldiff_terule_get_object_class(ter);
+ const char *default_type;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ default_type = poldiff_terule_get_modified_default(ter);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ case POLDIFF_FORM_MODIFIED:
+ default_type = poldiff_terule_get_original_default(ter);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ if (form == POLDIFF_FORM_MODIFIED && show_changes) {
+ const char *orig_default = poldiff_terule_get_original_default(ter);
+ const char *mod_default = poldiff_terule_get_modified_default(ter);
+ apol_str_appendf(&str, &str_len, "%s %s %s : %s +%s -%s", rule_type_str, source_type, target_type, object_class,
+ mod_default, orig_default);
+ } else
+ apol_str_appendf(&str, &str_len, "%s %s %s : %s %s", rule_type_str, source_type, target_type, object_class,
+ default_type);
+ return str;
+}
+
+static char *roletrans_to_string(const void *arg, poldiff_form_e form, int show_changes)
+{
+ poldiff_role_trans_t *rt = (poldiff_role_trans_t *) arg;
+ char *str = NULL;
+ size_t str_len = 0;
+ const char *source_role = poldiff_role_trans_get_source_role(rt);
+ const char *target_type = poldiff_role_trans_get_target_type(rt);
+ apol_str_appendf(&str, &str_len, "role_transition %s %s", source_role, target_type);
+ if (show_changes) {
+ const char *orig_default = poldiff_role_trans_get_original_default(rt);
+ const char *mod_default = poldiff_role_trans_get_modified_default(rt);
+
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ apol_str_appendf(&str, &str_len, " %s", mod_default);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ apol_str_appendf(&str, &str_len, " %s", orig_default);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ apol_str_appendf(&str, &str_len, " { +%s -%s }", mod_default, orig_default);
+ break;
+ default:
+ // should never get here:
+ assert(0);
+ }
+ }
+ return str;
+}
+
+static char *roleallow_to_string(const void *arg, poldiff_form_e form, int show_changes)
+{
+ poldiff_role_allow_t *rat = (poldiff_role_allow_t *) arg;
+ char *str = NULL, *orig_roles_str = NULL;
+ size_t str_len = 0, orig_roles_str_len = 0;
+ const char *name = poldiff_role_allow_get_name(rat);
+ const apol_vector_t *orig_roles;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ orig_roles = poldiff_role_allow_get_added_roles(rat);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ orig_roles = poldiff_role_allow_get_removed_roles(rat);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ orig_roles = poldiff_role_allow_get_unmodified_roles(rat);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ size_t i;
+ size_t num_orig_roles = apol_vector_get_size(orig_roles);
+ const char *fmt;
+ if (num_orig_roles > 1 || (show_changes && form == POLDIFF_FORM_MODIFIED))
+ fmt = "allow %s {%s }";
+ else
+ fmt = "allow %s%s";
+ for (i = 0; i < num_orig_roles; ++i) {
+ char *role = apol_vector_get_element(orig_roles, i);
+ apol_str_appendf(&orig_roles_str, &orig_roles_str_len, " %s", role);
+ }
+ if (show_changes && form == POLDIFF_FORM_MODIFIED) {
+ const apol_vector_t *added_role_v = poldiff_role_allow_get_added_roles(rat);
+ for (i = 0; i < apol_vector_get_size(added_role_v); ++i) {
+ char *added_role = apol_vector_get_element(added_role_v, i);
+ apol_str_appendf(&orig_roles_str, &orig_roles_str_len, " +%s", added_role);
+ }
+ const apol_vector_t *removed_role_v = poldiff_role_allow_get_removed_roles(rat);
+ for (i = 0; i < apol_vector_get_size(removed_role_v); ++i) {
+ char *removed_role = apol_vector_get_element(removed_role_v, i);
+ apol_str_appendf(&orig_roles_str, &orig_roles_str_len, " -%s", removed_role);
+ }
+ }
+ apol_str_appendf(&str, &str_len, fmt, name, orig_roles_str);
+ free(orig_roles_str);
+ return str;
+}
+
+void build_roleallow_vecs()
+{
+ char *str = NULL, *name_only = NULL;
+ size_t i;
+ const void *item = NULL;
+ const apol_vector_t *v = NULL;
+ v = poldiff_get_role_allow_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ item = apol_vector_get_element(v, i);
+ if (!item)
+ return;
+ poldiff_form_e form = poldiff_role_allow_get_form(item);
+ str = roleallow_to_string(item, form, 1);
+ if (!str)
+ break;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ name_only = roleallow_to_string(item, form, 0);
+ apol_vector_append(modified_name_only_v, name_only);
+ apol_vector_append(modified_v, str);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ }
+}
+
+void build_roletrans_vecs()
+{
+ added_type_rules_v = apol_vector_create(free);
+ removed_type_rules_v = apol_vector_create(free);
+ correct_added_type_rules_v = string_array_to_vector(added_roletrans_type);
+ correct_removed_type_rules_v = string_array_to_vector(removed_roletrans_type);
+
+ char *str = NULL, *name_only;
+ size_t i;
+ const void *item = NULL;
+ const apol_vector_t *v = NULL;
+ v = poldiff_get_role_trans_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ item = apol_vector_get_element(v, i);
+ if (!item)
+ return;
+ poldiff_form_e form = poldiff_role_trans_get_form(item);
+ str = roletrans_to_string(item, form, 1);
+ if (!str)
+ break;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ case POLDIFF_FORM_ADD_TYPE:
+ apol_vector_append(added_type_rules_v, str);
+ break;
+ case POLDIFF_FORM_REMOVE_TYPE:
+ apol_vector_append(removed_type_rules_v, str);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ name_only = roletrans_to_string(item, form, 0);
+ apol_vector_append(modified_name_only_v, name_only);
+ apol_vector_append(modified_v, str);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ }
+ int test_result;
+ size_t first_diff = 0;
+ apol_vector_sort(added_type_rules_v, compare_str, NULL);
+ apol_vector_sort(correct_added_type_rules_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(added_type_rules_v, correct_added_type_rules_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(added_type_rules_v, correct_added_type_rules_v, first_diff, "Added Rule (due to Type)");
+ }
+ apol_vector_sort(removed_type_rules_v, compare_str, NULL);
+ apol_vector_sort(correct_removed_type_rules_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(removed_type_rules_v, correct_removed_type_rules_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(removed_type_rules_v, correct_removed_type_rules_v, first_diff, "Removed Rule (due to Type)");
+ }
+ apol_vector_destroy(&added_type_rules_v);
+ apol_vector_destroy(&correct_added_type_rules_v);
+ apol_vector_destroy(&removed_type_rules_v);
+ apol_vector_destroy(&correct_removed_type_rules_v);
+}
+
+void build_terule_vecs()
+{
+ added_type_rules_v = apol_vector_create(free);
+ removed_type_rules_v = apol_vector_create(free);
+ correct_added_type_rules_v = string_array_to_vector(added_type_terules);
+ correct_removed_type_rules_v = string_array_to_vector(removed_type_terules);
+
+ size_t i;
+ char *str = NULL;
+ const void *item = NULL;
+ const apol_vector_t *member_v = NULL, *change_v = NULL, *trans_v = NULL;
+ member_v = poldiff_get_terule_vector_member(diff);
+ change_v = poldiff_get_terule_vector_change(diff);
+ trans_v = poldiff_get_terule_vector_trans(diff);
+ apol_vector_t *all_terules = apol_vector_create(NULL);
+ apol_vector_cat(all_terules, member_v);
+ apol_vector_cat(all_terules, change_v);
+ apol_vector_cat(all_terules, trans_v);
+
+ for (i = 0; i < apol_vector_get_size(all_terules); i++) {
+ item = apol_vector_get_element(all_terules, i);
+ if (!item)
+ return;
+ poldiff_form_e form = poldiff_terule_get_form(item);
+ str = terule_to_string(item, form, 1);
+ if (!str)
+ break;
+ char *name_only = NULL;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ case POLDIFF_FORM_ADD_TYPE:
+ apol_vector_append(added_type_rules_v, str);
+ break;
+ case POLDIFF_FORM_REMOVE_TYPE:
+ apol_vector_append(removed_type_rules_v, str);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ name_only = terule_to_string(item, form, 0);
+ apol_vector_append(modified_name_only_v, name_only);
+ apol_vector_append(modified_v, str);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ }
+ size_t first_diff = 0;
+ int test_result = 0;
+ apol_vector_sort(added_type_rules_v, compare_str, NULL);
+ apol_vector_sort(correct_added_type_rules_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(added_type_rules_v, correct_added_type_rules_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(added_type_rules_v, correct_added_type_rules_v, first_diff, "Added Rules (due to types)");
+ }
+
+ apol_vector_sort(removed_type_rules_v, compare_str, NULL);
+ apol_vector_sort(correct_removed_type_rules_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(removed_type_rules_v, correct_removed_type_rules_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(removed_type_rules_v, correct_removed_type_rules_v, first_diff, "Removed Rules (due to types)");
+ }
+ apol_vector_destroy(&all_terules);
+ apol_vector_destroy(&added_type_rules_v);
+ apol_vector_destroy(&correct_added_type_rules_v);
+ apol_vector_destroy(&removed_type_rules_v);
+ apol_vector_destroy(&correct_removed_type_rules_v);
+}
+
+void build_avrule_vecs()
+{
+ added_type_rules_v = apol_vector_create(free);
+ removed_type_rules_v = apol_vector_create(free);
+ correct_added_type_rules_v = string_array_to_vector(added_type_avrules);
+ correct_removed_type_rules_v = string_array_to_vector(removed_type_avrules);
+
+ size_t i;
+ char *str = NULL, *name_only = NULL;
+ const void *item = NULL;
+ const apol_vector_t *allow_v = NULL, *neverallow_v = NULL, *auditallow_v = NULL, *dontaudit_v = NULL;
+ apol_vector_t *all_avrules_v = apol_vector_create(NULL);
+
+ allow_v = poldiff_get_avrule_vector_allow(diff);
+ neverallow_v = poldiff_get_avrule_vector_neverallow(diff);
+ auditallow_v = poldiff_get_avrule_vector_auditallow(diff);
+ dontaudit_v = poldiff_get_avrule_vector_dontaudit(diff);
+
+ apol_vector_cat(all_avrules_v, allow_v);
+ apol_vector_cat(all_avrules_v, neverallow_v);
+ apol_vector_cat(all_avrules_v, auditallow_v);
+ apol_vector_cat(all_avrules_v, dontaudit_v);
+
+ for (i = 0; i < apol_vector_get_size(all_avrules_v); i++) {
+ item = apol_vector_get_element(all_avrules_v, i);
+ if (!item)
+ return;
+ poldiff_form_e form = poldiff_avrule_get_form(item);
+ str = avrule_to_string(item, form, 1);
+ if (!str)
+ break;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ case POLDIFF_FORM_ADD_TYPE:
+ apol_vector_append(added_type_rules_v, str);
+ break;
+ case POLDIFF_FORM_REMOVE_TYPE:
+ apol_vector_append(removed_type_rules_v, str);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ name_only = avrule_to_string(item, form, 0);
+ apol_vector_append(modified_name_only_v, name_only);
+ apol_vector_append(modified_v, str);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ }
+ size_t first_diff = 0;
+ apol_vector_sort(added_type_rules_v, compare_str, NULL);
+ apol_vector_sort(correct_added_type_rules_v, compare_str, NULL);
+ CU_ASSERT_FALSE(apol_vector_compare(added_type_rules_v, correct_added_type_rules_v, compare_str, NULL, &first_diff));
+
+ apol_vector_sort(removed_type_rules_v, compare_str, NULL);
+ apol_vector_sort(correct_removed_type_rules_v, compare_str, NULL);
+ CU_ASSERT_FALSE(apol_vector_compare(removed_type_rules_v, correct_removed_type_rules_v, compare_str, NULL, &first_diff));
+
+ apol_vector_destroy(&removed_type_rules_v);
+ apol_vector_destroy(&correct_removed_type_rules_v);
+ apol_vector_destroy(&added_type_rules_v);
+ apol_vector_destroy(&correct_added_type_rules_v);
+ apol_vector_destroy(&all_avrules_v);
+}
+
+void rules_avrules_tests()
+{
+ test_numbers_e test_num = RULES_AVRULE;
+ poldiff_test_answers_t *answers = init_answer_vectors(added_avrules, removed_avrules, unchanged_avrules, modified_avrules);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
+
+void rules_terules_tests()
+{
+ test_numbers_e test_num = RULES_TERULE;
+ poldiff_test_answers_t *answers = init_answer_vectors(added_terules, removed_terules, unchanged_terules, modified_terules);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
+
+void rules_roleallow_tests()
+{
+ test_numbers_e test_num = RULES_ROLEALLOW;
+ poldiff_test_answers_t *answers =
+ init_answer_vectors(added_roleallowrules, removed_roleallowrules, unchanged_roleallowrules,
+ modified_roleallowrules);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
+
+void rules_roletrans_tests()
+{
+ test_numbers_e test_num = RULES_ROLETRANS;
+ poldiff_test_answers_t *answers =
+ init_answer_vectors(added_roletrans_rules, removed_roletrans_rules, unchanged_roletrans_rules,
+ modified_roletrans_rules);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
+
+int rules_test_init()
+{
+ if (!(diff = init_poldiff(RULES_ORIG_POLICY, RULES_MOD_POLICY))) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
diff --git a/libpoldiff/tests/rules-tests.h b/libpoldiff/tests/rules-tests.h
new file mode 100644
index 0000000..34a30eb
--- /dev/null
+++ b/libpoldiff/tests/rules-tests.h
@@ -0,0 +1,40 @@
+/**
+ * @file
+ *
+ * Header file for libpoldiff's correctness of rules.
+ *
+ * @author Paul Rosenfeld prosenfeld@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 RULES_TEST
+#define RULES_TEST
+int rules_test_init();
+int rules_test_cleanup();
+
+void rules_avrules_tests();
+void rules_roleallow_tests();
+void rules_roletrans_tests();
+void rules_terules_tests();
+
+void build_avrule_vecs();
+void build_terule_vecs();
+void build_roletrans_vecs();
+void build_roleallow_vecs();
+
+#endif