From 44f7af068d53085eb1066454419ba7bc0e9b6cfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Tue, 5 Nov 2024 19:33:12 +0100 Subject: [PATCH] libselinux/utils: introduce selabel_compare MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a utility around selabel_cmp(3). Can be used by users to compare a pre-compiled fcontext file to an original text-based file context definition file. Can be used for development to verify compilation and parsing of the pre-compiled fcontext format works correctly. Signed-off-by: Christian Göttsche Acked-by: James Carter --- libselinux/utils/.gitignore | 1 + libselinux/utils/selabel_compare.c | 122 +++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 libselinux/utils/selabel_compare.c diff --git a/libselinux/utils/.gitignore b/libselinux/utils/.gitignore index b3311360..2e10b14f 100644 --- a/libselinux/utils/.gitignore +++ b/libselinux/utils/.gitignore @@ -16,6 +16,7 @@ getseuser matchpathcon policyvers sefcontext_compile +selabel_compare selabel_digest selabel_get_digests_all_partial_matches selabel_lookup diff --git a/libselinux/utils/selabel_compare.c b/libselinux/utils/selabel_compare.c new file mode 100644 index 00000000..9ca6eff1 --- /dev/null +++ b/libselinux/utils/selabel_compare.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include + +#include + + +static void usage(const char *progname) +{ + fprintf(stderr, + "usage: %s [-b backend] [-v] file1 file2\n\n" + "Where:\n\t" + "-b The backend - \"file\", \"media\", \"x\", \"db\" or \"prop\" (defaults to \"file\")\n\t" + "-v Validate entries against loaded policy.\n\t" + "file1/file2 Files containing the specs.\n", + progname); +} + +static int compare(const char *file1, const char *file2, const char *validate, unsigned int backend) +{ + struct selabel_handle *hnd1, *hnd2; + const struct selinux_opt selabel_option1[] = { + { SELABEL_OPT_PATH, file1 }, + { SELABEL_OPT_VALIDATE, validate } + }; + const struct selinux_opt selabel_option2[] = { + { SELABEL_OPT_PATH, file2 }, + { SELABEL_OPT_VALIDATE, validate } + }; + enum selabel_cmp_result result; + + hnd1 = selabel_open(backend, selabel_option1, 2); + if (!hnd1) { + fprintf(stderr, "ERROR: selabel_open - Could not obtain handle for %s: %m\n", file1); + return EXIT_FAILURE; + } + + hnd2 = selabel_open(backend, selabel_option2, 2); + if (!hnd2) { + fprintf(stderr, "ERROR: selabel_open - Could not obtain handle for %s: %m\n", file2); + selabel_close(hnd1); + return EXIT_FAILURE; + } + + result = selabel_cmp(hnd1, hnd2); + + selabel_close(hnd2); + selabel_close(hnd1); + + switch (result) { + case SELABEL_SUBSET: + printf("spec %s is a subset of spec %s\n", file1, file2); + break; + case SELABEL_EQUAL: + printf("spec %s is equal to spec %s\n", file1, file2); + break; + case SELABEL_SUPERSET: + printf("spec %s is a superset of spec %s\n", file1, file2); + break; + case SELABEL_INCOMPARABLE: + printf("spec %s is uncomparable to spec %s\n", file1, file2); + break; + default: + fprintf(stderr, "ERROR: selabel_cmp - Unexpected result %d\n", result); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) +{ + unsigned int backend = SELABEL_CTX_FILE; + int opt; + const char *validate = NULL, *file1 = NULL, *file2 = NULL; + + if (argc < 3) { + usage(argv[0]); + return EXIT_FAILURE; + } + + while ((opt = getopt(argc, argv, "b:v")) > 0) { + switch (opt) { + case 'b': + if (!strcasecmp(optarg, "file")) { + backend = SELABEL_CTX_FILE; + } else if (!strcmp(optarg, "media")) { + backend = SELABEL_CTX_MEDIA; + } else if (!strcmp(optarg, "x")) { + backend = SELABEL_CTX_X; + } else if (!strcmp(optarg, "db")) { + backend = SELABEL_CTX_DB; + } else if (!strcmp(optarg, "prop")) { + backend = SELABEL_CTX_ANDROID_PROP; + } else if (!strcmp(optarg, "service")) { + backend = SELABEL_CTX_ANDROID_SERVICE; + } else { + fprintf(stderr, "Unknown backend: %s\n", optarg); + usage(argv[0]); + return EXIT_FAILURE; + } + break; + case 'v': + validate = (char *)1; + break; + default: + usage(argv[0]); + return EXIT_FAILURE; + } + } + + if (argc != optind + 2) { + usage(argv[0]); + return EXIT_FAILURE; + } + + file1 = argv[optind++]; + file2 = argv[optind]; + + return compare(file1, file2, validate, backend); +}