On Mon, Aug 14, 2023 at 9:42 AM Christian Göttsche <cgzones@xxxxxxxxxxxxxx> wrote: > > 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 <cgzones@xxxxxxxxxxxxxx> > --- > libselinux/utils/.gitignore | 1 + > libselinux/utils/selabel_compare.c | 119 +++++++++++++++++++++++++++++ > 2 files changed, 120 insertions(+) > create mode 100644 libselinux/utils/selabel_compare.c > > diff --git a/libselinux/utils/.gitignore b/libselinux/utils/.gitignore > index a92e1e94..4e2dfba8 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..f4325f7e > --- /dev/null > +++ b/libselinux/utils/selabel_compare.c > @@ -0,0 +1,119 @@ > +#include <getopt.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > + > +#include <selinux/label.h> > + > + > +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); > +} > + > +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]; > + > + { To me, having a block like this means that the stuff below should be in a helper function. Everything else looks good. Jim > + 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 uncompareable to spec %s\n", file1, file2); > + break; > + default: > + fprintf(stderr, "ERROR: selabel_cmp - Unexpected result %d\n", result); > + return EXIT_FAILURE; > + } > + > + return EXIT_SUCCESS; > + } > +} > -- > 2.40.1 >