Message ID | 20230814132025.45364-14-cgzones@googlemail.com (mailing list archive) |
---|---|
State | New, archived |
Delegated to: | Petr Lautrbach |
Headers | show |
Series | libselinux: rework selabel_file(5) database | expand |
On Mon, Aug 14, 2023 at 9:42 AM Christian Göttsche <cgzones@googlemail.com> 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@googlemail.com> > --- > 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 >
On Thu, 5 Oct 2023 at 17:29, James Carter <jwcart2@gmail.com> wrote: > > On Mon, Aug 14, 2023 at 9:42 AM Christian Göttsche > <cgzones@googlemail.com> 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@googlemail.com> > > --- > > 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 > I didn't want to split the function to complicate the reasoning by splitting the control flow, introducing 4 function parameters, and adding return value handling. Do you still prefer to split? (I'll send a v2 regardless for a typo below) > > > + 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 > >
On Wed, Nov 1, 2023 at 12:47 PM Christian Göttsche <cgzones@googlemail.com> wrote: > > On Thu, 5 Oct 2023 at 17:29, James Carter <jwcart2@gmail.com> wrote: > > > > On Mon, Aug 14, 2023 at 9:42 AM Christian Göttsche > > <cgzones@googlemail.com> 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@googlemail.com> > > > --- > > > 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 > > > > I didn't want to split the function to complicate the reasoning by > splitting the control flow, introducing 4 function parameters, and > adding return value handling. > Do you still prefer to split? > (I'll send a v2 regardless for a typo below) > Yes, I would. Something like this: static enum selabel_cmp_result compare(unsigned int backend, const char *file1, const char *file2, const char *validate) { 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); return result; } ... int main(int argc, char *argv[]) { ... result = compare(backend, file1, file2, validate); 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; } You can probably think of a better name for the function. Thanks, 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 > > >
On Wed, 1 Nov 2023 at 21:57, James Carter <jwcart2@gmail.com> wrote: > > On Wed, Nov 1, 2023 at 12:47 PM Christian Göttsche > <cgzones@googlemail.com> wrote: > > > > On Thu, 5 Oct 2023 at 17:29, James Carter <jwcart2@gmail.com> wrote: > > > > > > On Mon, Aug 14, 2023 at 9:42 AM Christian Göttsche > > > <cgzones@googlemail.com> 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@googlemail.com> > > > > --- > > > > 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 > > > > > > > I didn't want to split the function to complicate the reasoning by > > splitting the control flow, introducing 4 function parameters, and > > adding return value handling. > > Do you still prefer to split? > > (I'll send a v2 regardless for a typo below) > > > > Yes, I would. Fair enough. I am not going to send a standalone version of this patch though, since the current compare function can not handle compiled regular expressions, e.g.: $ DESTDIR=/tmp/destdir/ ../selinux/scripts/env_use_destdir /tmp/destdir/usr/sbin/selabel_compare file_contexts.bin file_contexts.bin selabel_cmp: mismatched regex on entry 0: (/.*, 0, system_u:object_r:default_t) vs entry 0: (/.*, 0, system_u:object_r:default_t) spec file_contexts.bin is uncompareable to spec file_contexts.bin > Something like this: > > static enum selabel_cmp_result compare(unsigned int backend, const > char *file1, const char *file2, const char *validate) > { > 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); > return result; > } > > ... > > int main(int argc, char *argv[]) > { > ... > result = compare(backend, file1, file2, validate); > 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; > } > > You can probably think of a better name for the function. > > Thanks, > 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 > > > >
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]; + + { + 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; + } +}
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@googlemail.com> --- libselinux/utils/.gitignore | 1 + libselinux/utils/selabel_compare.c | 119 +++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 libselinux/utils/selabel_compare.c