diff mbox series

[RFC,v2,13/27] libselinux/utils: introduce selabel_compare

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

Commit Message

Christian Göttsche Aug. 14, 2023, 1:20 p.m. UTC
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

Comments

James Carter Oct. 5, 2023, 3:29 p.m. UTC | #1
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
>
Christian Göttsche Nov. 1, 2023, 4:47 p.m. UTC | #2
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
> >
James Carter Nov. 1, 2023, 8:57 p.m. UTC | #3
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
> > >
Christian Göttsche Nov. 3, 2023, 6:24 p.m. UTC | #4
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 mbox series

Patch

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;
+	}
+}