/** * @file * Command line frontend 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 program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #define COPYRIGHT_INFO "Copyright (C) 2004-2007 Tresys Technology, LLC" enum opt_values { DIFF_LEVEL = 256, DIFF_CATEGORY, DIFF_AUDITALLOW, DIFF_DONTAUDIT, DIFF_NEVERALLOW, DIFF_TYPE_CHANGE, DIFF_TYPE_MEMBER, DIFF_TYPE_TRANS, DIFF_ROLE_TRANS, DIFF_ROLE_ALLOW, DIFF_RANGE_TRANS, OPT_STATS }; /* command line options struct */ static struct option const longopts[] = { {"class", no_argument, NULL, 'c'}, {"level", no_argument, NULL, DIFF_LEVEL}, {"category", no_argument, NULL, DIFF_CATEGORY}, {"type", no_argument, NULL, 't'}, {"attribute", no_argument, NULL, 'a'}, {"role", no_argument, NULL, 'r'}, {"user", no_argument, NULL, 'u'}, {"bool", no_argument, NULL, 'b'}, {"allow", no_argument, NULL, 'A'}, {"auditallow", no_argument, NULL, DIFF_AUDITALLOW}, {"dontaudit", no_argument, NULL, DIFF_DONTAUDIT}, {"neverallow", no_argument, NULL, DIFF_NEVERALLOW}, {"type_change", no_argument, NULL, DIFF_TYPE_CHANGE}, {"type_trans", no_argument, NULL, DIFF_TYPE_TRANS}, {"type_member", no_argument, NULL, DIFF_TYPE_MEMBER}, {"role_trans", no_argument, NULL, DIFF_ROLE_TRANS}, {"role_allow", no_argument, NULL, DIFF_ROLE_ALLOW}, {"range_trans", no_argument, NULL, DIFF_RANGE_TRANS}, {"stats", no_argument, NULL, OPT_STATS}, {"quiet", no_argument, NULL, 'q'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {NULL, 0, NULL, 0} }; static void usage(const char *prog_name, int brief) { printf("Usage: %s [OPTIONS] ORIGINAL_POLICY ; MODIFIED_POLICY\n\n", prog_name); if (brief) { printf("\tTry %s --help for more help.\n\n", prog_name); return; } printf("Semantically differentiate two policies. By default, all supported\n"); printf("policy elements sans neverallows are examined. The following options\n"); printf("are available:\n\n"); printf(" -c, --class object class and common permission definitions\n"); printf(" --level MLS level definitions\n"); printf(" --category MLS category definitions\n"); printf(" -t, --type type definitions\n"); printf(" -a, --attribute attribute definitions\n"); printf(" -r, --role role definitions\n"); printf(" -u, --user user definitions\n"); printf(" -b, --bool boolean definitions and default values\n"); printf(" -A, --allow allow rules\n"); printf(" --auditallow auditallow rules\n"); printf(" --dontaudit dontaudit rules\n"); printf(" --neverallow neverallow rules\n"); printf(" --type_change type_change rules\n"); printf(" --type_member type_member rules\n"); printf(" --type_trans type_transition rules\n"); printf(" --role_trans role_transition rules\n"); printf(" --role_allow role allow rules\n"); printf(" --range_trans range_transition rules\n"); printf("\n"); printf(" -q, --quiet suppress status output for elements with no differences\n"); printf(" --stats print only statistics\n"); printf(" -h, --help print this help text and exit\n"); printf(" -V, --version print version information and exit\n\n"); } static void print_diff_string(const char *str, unsigned int indent_level) { const char *c = str; unsigned int i; static const char *indent = " "; for (i = 0; i < indent_level; i++) printf("%s", indent); for (; *c; c++) { if (*c == '\n') { if (*(c + 1) == '\0') break; printf("%c", *c); for (i = 0; i < indent_level; i++) printf("%s", indent); } else if (*c == '\t') { printf("%s", indent); } else { printf("%c", *c); } } } static void print_rule_section(const poldiff_t * diff, const poldiff_component_record_t * rec, const apol_vector_t * v, poldiff_form_e form) { int i; char *str = NULL; const void *item1; for (i = 0; i < apol_vector_get_size(v); i++) { item1 = apol_vector_get_element(v, i); if (poldiff_component_record_get_form_fn(rec) (item1) == form) { if ((str = poldiff_component_record_get_to_string_fn(rec) (diff, item1)) == NULL) { return; } print_diff_string(str, 1); printf("\n"); free(str); str = NULL; } } } #define PRINT_ADDED_REMOVED 1 #define PRINT_MODIFIED 2 #define PRINT_ALL 4 static void print_rule_diffs(const poldiff_t * diff, const poldiff_component_record_t * rec, int stats_only, const char *name, uint32_t flags, apol_vector_comp_func sort_by) { const apol_vector_t *internal_v = NULL; apol_vector_t *v = NULL; size_t stats[5] = { 0, 0, 0, 0, 0 }; if (!rec || !diff) return; poldiff_component_record_get_stats_fn(rec) (diff, stats); if (flags == PRINT_ADDED_REMOVED) { printf("%s (Added %zd, Removed %zd)\n", name, stats[0], stats[1]); } else if (flags == PRINT_MODIFIED) { printf("%s (Added %zd, Removed %zd, Modified %zd)\n", name, stats[0], stats[1], stats[2]); } else if (flags == PRINT_ALL) { printf("%s (Added %zd, Added New Type %zd, Removed %zd, Removed Missing Type %zd, Modified %zd)\n", name, stats[0], stats[3], stats[1], stats[4], stats[2]); } else { fprintf(stderr, "Error, unhandled flag type\n"); } if (stats_only) return; if ((internal_v = poldiff_component_record_get_results_fn(rec) (diff)) == NULL) { return; } if (!(v = apol_vector_create_from_vector(internal_v, NULL, NULL, NULL))) { perror("Error printig results"); return; } if (sort_by) { apol_vector_sort(v, sort_by, NULL); } printf(" Added %s: %zd\n", name, stats[0]); print_rule_section(diff, rec, v, POLDIFF_FORM_ADDED); if (flags == PRINT_ALL) { printf(" Added %s because of new type: %zd\n", name, stats[3]); print_rule_section(diff, rec, v, POLDIFF_FORM_ADD_TYPE); } printf(" Removed %s: %zd\n", name, stats[1]); print_rule_section(diff, rec, v, POLDIFF_FORM_REMOVED); if (flags == PRINT_ALL) { printf(" Removed %s because of missing type: %zd\n", name, stats[4]); print_rule_section(diff, rec, v, POLDIFF_FORM_REMOVE_TYPE); } if (flags == PRINT_MODIFIED || flags == PRINT_ALL) { printf(" Modified %s: %zd\n", name, stats[2]); print_rule_section(diff, rec, v, POLDIFF_FORM_MODIFIED); } printf("\n"); apol_vector_destroy(&v); return; } static void print_class_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_CLASSES), stats_only, "Classes", PRINT_MODIFIED, NULL); return; } static void print_bool_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_BOOLS), stats_only, "Booleans", PRINT_MODIFIED, NULL); return; } static void print_common_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_COMMONS), stats_only, "Commons", PRINT_MODIFIED, NULL); return; } static void print_level_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_LEVELS), stats_only, "Levels", PRINT_MODIFIED, NULL); return; } static void print_cat_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_CATS), stats_only, "Categories", PRINT_MODIFIED, NULL); return; } static void print_role_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_ROLES), stats_only, "Roles", PRINT_MODIFIED, NULL); return; } static void print_user_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_USERS), stats_only, "Users", PRINT_MODIFIED, NULL); return; } static void print_avallow_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_AVALLOW), stats_only, "AV-Allow Rules", PRINT_ALL, NULL); } static void print_avauditallow_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_AVAUDITALLOW), stats_only, "AV-Audit Allow Rules", PRINT_ALL, NULL); } static void print_avdontaudit_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_AVDONTAUDIT), stats_only, "AV-Don't Audit Rules", PRINT_ALL, NULL); } static void print_avneverallow_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_AVNEVERALLOW), stats_only, "AV-Never Allow Rules", PRINT_ALL, NULL); } static void print_role_allow_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_ROLE_ALLOWS), stats_only, "Role Allow Rules", PRINT_MODIFIED, NULL); } static void print_role_trans_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_ROLE_TRANS), stats_only, "Role Transitions", PRINT_ALL, NULL); } static void print_range_trans_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_RANGE_TRANS), stats_only, "Range Transitions", PRINT_MODIFIED, NULL); } /** compare the names for two poldiff_type_t objects. * used to sort items prior to display. */ static int type_name_cmp(const void *a, const void *b, void *user_data __attribute__ ((unused))) { poldiff_type_t *ta = (poldiff_type_t *) a; poldiff_type_t *tb = (poldiff_type_t *) b; if (ta == NULL || tb == NULL) return -1; return strcmp(poldiff_type_get_name(ta), poldiff_type_get_name(tb)); } static void print_type_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_TYPES), stats_only, "Types", PRINT_MODIFIED, type_name_cmp); } static void print_attrib_diffs(const poldiff_t * diff, int stats_only) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_ATTRIBS), stats_only, "Attributes", PRINT_MODIFIED, NULL); } static size_t get_diff_total(const poldiff_t * diff, uint32_t flags) { size_t total = 0; uint32_t i; size_t stats[5] = { 0, 0, 0, 0, 0 }; if (!diff || !flags) return 0; /* for all 32 bits possible in flags */ for (i = 0x80000000; i; i = i >> 1) { if (flags & i) { poldiff_get_stats(diff, i, stats); total += (stats[0] + stats[1] + stats[2] + stats[3] + stats[4]); } } return total; } static void print_diff(const poldiff_t * diff, uint32_t flags, int stats, int quiet) { if (flags & POLDIFF_DIFF_CLASSES && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_CLASSES))) { print_class_diffs(diff, stats); } if (flags & POLDIFF_DIFF_COMMONS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_COMMONS))) { print_common_diffs(diff, stats); } if (flags & POLDIFF_DIFF_LEVELS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_LEVELS))) { print_level_diffs(diff, stats); } if (flags & POLDIFF_DIFF_CATS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_CATS))) { print_cat_diffs(diff, stats); } if (flags & POLDIFF_DIFF_TYPES && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_TYPES))) { print_type_diffs(diff, stats); } if (flags & POLDIFF_DIFF_ATTRIBS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_ATTRIBS))) { print_attrib_diffs(diff, stats); } if (flags & POLDIFF_DIFF_ROLES && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_ROLES))) { print_role_diffs(diff, stats); } if (flags & POLDIFF_DIFF_USERS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_USERS))) { print_user_diffs(diff, stats); } if (flags & POLDIFF_DIFF_BOOLS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_BOOLS))) { print_bool_diffs(diff, stats); } if (flags & POLDIFF_DIFF_AVALLOW && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_AVALLOW))) { print_avallow_diffs(diff, stats); } if (flags & POLDIFF_DIFF_AVAUDITALLOW && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_AVAUDITALLOW))) { print_avauditallow_diffs(diff, stats); } if (flags & POLDIFF_DIFF_AVDONTAUDIT && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_AVDONTAUDIT))) { print_avdontaudit_diffs(diff, stats); } if (flags & POLDIFF_DIFF_AVNEVERALLOW && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_AVNEVERALLOW))) { print_avneverallow_diffs(diff, stats); } if (flags & POLDIFF_DIFF_TECHANGE && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_TECHANGE))) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_TECHANGE), stats, "TE type_change", PRINT_ALL, NULL); } if (flags & POLDIFF_DIFF_TEMEMBER && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_TEMEMBER))) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_TEMEMBER), stats, "TE type_member", PRINT_ALL, NULL); } if (flags & POLDIFF_DIFF_TETRANS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_TETRANS))) { print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_TETRANS), stats, "TE type_trans", PRINT_ALL, NULL); } if (flags & POLDIFF_DIFF_ROLE_ALLOWS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_ROLE_ALLOWS))) { print_role_allow_diffs(diff, stats); } if (flags & POLDIFF_DIFF_ROLE_TRANS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_ROLE_TRANS))) { print_role_trans_diffs(diff, stats); } if (flags & POLDIFF_DIFF_RANGE_TRANS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_RANGE_TRANS))) { print_range_trans_diffs(diff, stats); } } int main(int argc, char **argv) { int optc = 0, quiet = 0, stats = 0, default_all = 0; uint32_t flags = 0; apol_policy_t *orig_policy = NULL, *mod_policy = NULL; apol_policy_path_type_e orig_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; char *orig_base_path = NULL; apol_vector_t *orig_module_paths = NULL; apol_policy_path_t *orig_pol_path = NULL; apol_policy_path_type_e mod_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; char *mod_base_path = NULL; apol_vector_t *mod_module_paths = NULL; apol_policy_path_t *mod_pol_path = NULL; poldiff_t *diff = NULL; size_t total = 0; while ((optc = getopt_long(argc, argv, "ctarubAqhV", longopts, NULL)) != -1) { switch (optc) { case 0: break; case 'c': flags |= (POLDIFF_DIFF_CLASSES | POLDIFF_DIFF_COMMONS); break; case DIFF_LEVEL: flags |= POLDIFF_DIFF_LEVELS; break; case DIFF_CATEGORY: flags |= POLDIFF_DIFF_CATS; break; case 't': flags |= POLDIFF_DIFF_TYPES; break; case 'a': flags |= POLDIFF_DIFF_ATTRIBS; break; case 'r': flags |= POLDIFF_DIFF_ROLES; break; case 'u': flags |= POLDIFF_DIFF_USERS; break; case 'b': flags |= POLDIFF_DIFF_BOOLS; break; case 'A': flags |= POLDIFF_DIFF_AVALLOW; break; case DIFF_AUDITALLOW: flags |= POLDIFF_DIFF_AVAUDITALLOW; break; case DIFF_DONTAUDIT: flags |= POLDIFF_DIFF_AVDONTAUDIT; break; case DIFF_NEVERALLOW: flags |= POLDIFF_DIFF_AVNEVERALLOW; break; case DIFF_TYPE_CHANGE: flags |= POLDIFF_DIFF_TECHANGE; break; case DIFF_TYPE_MEMBER: flags |= POLDIFF_DIFF_TEMEMBER; break; case DIFF_TYPE_TRANS: flags |= POLDIFF_DIFF_TETRANS; break; case DIFF_ROLE_ALLOW: flags |= POLDIFF_DIFF_ROLE_ALLOWS; break; case DIFF_ROLE_TRANS: flags |= POLDIFF_DIFF_ROLE_TRANS; break; case DIFF_RANGE_TRANS: flags |= POLDIFF_DIFF_RANGE_TRANS; break; case OPT_STATS: stats = 1; break; case 'q': quiet = 1; break; case 'h': usage(argv[0], 0); exit(0); case 'V': printf("sediff %s\n%s\n", VERSION, COPYRIGHT_INFO); exit(0); default: usage(argv[0], 1); exit(1); } } if (!flags) { flags = POLDIFF_DIFF_ALL & ~POLDIFF_DIFF_AVNEVERALLOW; default_all = 1; } if (argc - optind < 2) { usage(argv[0], 1); exit(1); } if (!strcmp(";", argv[optind])) { ERR(NULL, "%s", "Missing path to original policy."); goto err; } orig_base_path = argv[optind++]; orig_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; if (!(orig_module_paths = apol_vector_create(NULL))) { ERR(NULL, "%s", strerror(errno)); goto err; } for (; argc - optind; optind++) { if (!strcmp(";", argv[optind])) { optind++; break; } if (apol_vector_append(orig_module_paths, (void *)argv[optind])) { ERR(NULL, "Error loading module %s", argv[optind]); goto err; } orig_path_type = APOL_POLICY_PATH_TYPE_MODULAR; } if (apol_file_is_policy_path_list(orig_base_path) > 0) { orig_pol_path = apol_policy_path_create_from_file(orig_base_path); if (!orig_pol_path) { ERR(NULL, "%s", "invalid policy list"); goto err; } } else { orig_pol_path = apol_policy_path_create(orig_path_type, orig_base_path, orig_module_paths); if (!orig_pol_path) { ERR(NULL, "%s", strerror(errno)); goto err; } } apol_vector_destroy(&orig_module_paths); if (argc - optind == 0) { ERR(NULL, "%s", "Missing path to modified policy."); goto err; } mod_base_path = argv[optind++]; mod_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; if (!(mod_module_paths = apol_vector_create(NULL))) { ERR(NULL, "%s", strerror(errno)); goto err; } for (; argc - optind; optind++) { if (apol_vector_append(mod_module_paths, (void *)argv[optind])) { ERR(NULL, "Error loading module %s", argv[optind]); goto err; } mod_path_type = APOL_POLICY_PATH_TYPE_MODULAR; } if (apol_file_is_policy_path_list(mod_base_path) > 0) { mod_pol_path = apol_policy_path_create_from_file(mod_base_path); if (!mod_pol_path) { ERR(NULL, "%s", "invalid policy list"); goto err; } } else { mod_pol_path = apol_policy_path_create(mod_path_type, mod_base_path, mod_module_paths); if (!mod_pol_path) { ERR(NULL, "%s", strerror(errno)); goto err; } } apol_vector_destroy(&mod_module_paths); int policy_opt = 0; if (!(flags & POLDIFF_DIFF_AVNEVERALLOW)) { policy_opt |= QPOL_POLICY_OPTION_NO_NEVERALLOWS; } if (!(flags & POLDIFF_DIFF_RULES)) { policy_opt |= QPOL_POLICY_OPTION_NO_RULES; } orig_policy = apol_policy_create_from_policy_path(orig_pol_path, policy_opt, NULL, NULL); if (!orig_policy) { ERR(NULL, "%s", strerror(errno)); goto err; } mod_policy = apol_policy_create_from_policy_path(mod_pol_path, policy_opt, NULL, NULL); if (!mod_policy) { ERR(NULL, "%s", strerror(errno)); goto err; } qpol_policy_t *orig_qpol = apol_policy_get_qpol(orig_policy); qpol_policy_t *mod_qpol = apol_policy_get_qpol(mod_policy); /* we disable attribute diffs if either policy does not * support attribute names because the fake attribute names * won't make sense */ if ((flags & POLDIFF_DIFF_ATTRIBS) && (!(qpol_policy_has_capability(orig_qpol, QPOL_CAP_ATTRIB_NAMES)) || !(qpol_policy_has_capability(mod_qpol, QPOL_CAP_ATTRIB_NAMES)))) { flags &= ~POLDIFF_DIFF_ATTRIBS; WARN(NULL, "%s", "Attribute diffs are not supported for current policies."); } /* we disable MLS diffs if both policies do not support MLS * but do not warn if it was implicitly requested for two * non-MLS policies */ if ((flags & POLDIFF_DIFF_MLS) && (!(qpol_policy_has_capability(orig_qpol, QPOL_CAP_MLS)) && !(qpol_policy_has_capability(mod_qpol, QPOL_CAP_MLS)))) { flags &= ~(POLDIFF_DIFF_MLS); if (!default_all) { WARN(NULL, "%s", "MLS diffs are not supported for current policies."); } } /* default callback for error handling is sufficient here */ if (!(diff = poldiff_create(orig_policy, mod_policy, NULL, NULL))) { ERR(NULL, "%s", strerror(errno)); goto err; } /* poldiff now owns the policies */ orig_policy = mod_policy = NULL; if (poldiff_run(diff, flags)) { goto err; } print_diff(diff, flags, stats, quiet); total = get_diff_total(diff, flags); apol_policy_path_destroy(&orig_pol_path); apol_policy_path_destroy(&mod_pol_path); poldiff_destroy(&diff); if (total) return 1; else return 0; 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); apol_vector_destroy(&orig_module_paths); apol_vector_destroy(&mod_module_paths); poldiff_destroy(&diff); return 1; }