diff options
Diffstat (limited to 'libpoldiff')
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 |