diff mbox series

[v5] Add security_validatetrans support

Message ID 20190405190102.13987-1-joshua.brindle@crunchydata.com (mailing list archive)
State Accepted
Headers show
Series [v5] Add security_validatetrans support | expand

Commit Message

Joshua Brindle April 5, 2019, 7:01 p.m. UTC
It seems validatetrans support was never added to libselinux, despite being added to
selinuxfs in kernel version 4.5

There is a utility to test, however the targeted policy has no validatetrans rules so some must be added:

$ cat validatetrans.cil
(mlsvalidatetrans db_table (and (or (or (or (eq l1 l2) (and (eq t3 unconfined_t) (domby l1 l2))) (and (eq t3 unconfined_t) (dom l1 l2))) (and (eq t3 unconfined_t) (incomp l1 l2))) (or (or (or (eq l1 h2) (and (eq t3 unconfined_t) (domby h1 h2))) (and (eq t3 unconfined_t) (dom h1 h2))) (and (eq t3 unconfined_t) (incomp h1 h2)))))

$ sudo semodule -i validatetrans.cil

$ ./validatetrans system_u:system_r:kernel_t:s0 system_u:system_r:init_t:s0:c0 db_table system_u:system_r: # invalid context here
opening /sys/fs/selinux/validatetrans
security_validatetrans returned -1 errno: Invalid argument

$ ./validatetrans system_u:system_r:kernel_t:s0 system_u:system_r:init_t:s0:c0 db_table system_u:system_r:init_t:s0
opening /sys/fs/selinux/validatetrans
security_validatetrans returned -1 errno: Operation not permitted

$ ./validatetrans system_u:system_r:kernel_t:s0 system_u:system_r:init_t:s0:c0 db_table system_u:system_r:unconfined_t:s0
opening /sys/fs/selinux/validatetrans
security_validatetrans returned 0 errno: Success

Signed-off-by: Joshua Brindle <joshua.brindle@crunchydata.com>
---
 libselinux/include/selinux/selinux.h          | 13 +++
 libselinux/man/man3/security_compute_av.3     | 13 ++-
 libselinux/man/man3/security_validatetrans.c  |  1 +
 .../man/man3/security_validatetrans_raw.c     |  1 +
 libselinux/src/selinux_internal.h             |  2 +
 libselinux/src/validatetrans.c                | 94 +++++++++++++++++++
 libselinux/utils/.gitignore                   |  1 +
 libselinux/utils/validatetrans.c              | 30 ++++++
 8 files changed, 154 insertions(+), 1 deletion(-)
 create mode 100644 libselinux/man/man3/security_validatetrans.c
 create mode 100644 libselinux/man/man3/security_validatetrans_raw.c
 create mode 100644 libselinux/src/validatetrans.c
 create mode 100644 libselinux/utils/validatetrans.c

Comments

William Roberts April 5, 2019, 7:04 p.m. UTC | #1
On Fri, Apr 5, 2019 at 12:01 PM Joshua Brindle
<joshua.brindle@crunchydata.com> wrote:
>
> It seems validatetrans support was never added to libselinux, despite being added to
> selinuxfs in kernel version 4.5
>
> There is a utility to test, however the targeted policy has no validatetrans rules so some must be added:
>
> $ cat validatetrans.cil
> (mlsvalidatetrans db_table (and (or (or (or (eq l1 l2) (and (eq t3 unconfined_t) (domby l1 l2))) (and (eq t3 unconfined_t) (dom l1 l2))) (and (eq t3 unconfined_t) (incomp l1 l2))) (or (or (or (eq l1 h2) (and (eq t3 unconfined_t) (domby h1 h2))) (and (eq t3 unconfined_t) (dom h1 h2))) (and (eq t3 unconfined_t) (incomp h1 h2)))))
>
> $ sudo semodule -i validatetrans.cil
>
> $ ./validatetrans system_u:system_r:kernel_t:s0 system_u:system_r:init_t:s0:c0 db_table system_u:system_r: # invalid context here
> opening /sys/fs/selinux/validatetrans
> security_validatetrans returned -1 errno: Invalid argument
>
> $ ./validatetrans system_u:system_r:kernel_t:s0 system_u:system_r:init_t:s0:c0 db_table system_u:system_r:init_t:s0
> opening /sys/fs/selinux/validatetrans
> security_validatetrans returned -1 errno: Operation not permitted
>
> $ ./validatetrans system_u:system_r:kernel_t:s0 system_u:system_r:init_t:s0:c0 db_table system_u:system_r:unconfined_t:s0
> opening /sys/fs/selinux/validatetrans
> security_validatetrans returned 0 errno: Success
>
> Signed-off-by: Joshua Brindle <joshua.brindle@crunchydata.com>
> ---
>  libselinux/include/selinux/selinux.h          | 13 +++
>  libselinux/man/man3/security_compute_av.3     | 13 ++-
>  libselinux/man/man3/security_validatetrans.c  |  1 +
>  .../man/man3/security_validatetrans_raw.c     |  1 +
>  libselinux/src/selinux_internal.h             |  2 +
>  libselinux/src/validatetrans.c                | 94 +++++++++++++++++++
>  libselinux/utils/.gitignore                   |  1 +
>  libselinux/utils/validatetrans.c              | 30 ++++++
>  8 files changed, 154 insertions(+), 1 deletion(-)
>  create mode 100644 libselinux/man/man3/security_validatetrans.c
>  create mode 100644 libselinux/man/man3/security_validatetrans_raw.c
>  create mode 100644 libselinux/src/validatetrans.c
>  create mode 100644 libselinux/utils/validatetrans.c
>
> diff --git a/libselinux/include/selinux/selinux.h b/libselinux/include/selinux/selinux.h
> index a34d54fc..f54f236b 100644
> --- a/libselinux/include/selinux/selinux.h
> +++ b/libselinux/include/selinux/selinux.h
> @@ -255,6 +255,19 @@ extern int security_compute_user_raw(const char * scon,
>                                      const char *username,
>                                      char *** con);
>
> +/* Validate a transition. This determines whether a transition from scon to newcon
> +   using tcon as the target for object class tclass is valid in the loaded policy.
> +   This checks against the mlsvalidatetrans and validatetrans constraints in the loaded policy.
> +   Returns 0 if allowed and -1 if an error occured with errno set */
> +extern int security_validatetrans(const char *scon,
> +                                 const char *tcon,
> +                                 security_class_t tclass,
> +                                 const char *newcon);
> +extern int security_validatetrans_raw(const char *scon,
> +                                     const char *tcon,
> +                                     security_class_t tclass,
> +                                     const char *newcon);
> +
>  /* Load a policy configuration. */
>  extern int security_load_policy(void *data, size_t len);
>
> diff --git a/libselinux/man/man3/security_compute_av.3 b/libselinux/man/man3/security_compute_av.3
> index 2aade5fe..a7181bed 100644
> --- a/libselinux/man/man3/security_compute_av.3
> +++ b/libselinux/man/man3/security_compute_av.3
> @@ -1,7 +1,7 @@
>  .TH "security_compute_av" "3" "1 January 2004" "russell@coker.com.au" "SELinux API documentation"
>  .SH "NAME"
>  security_compute_av, security_compute_av_flags, security_compute_create, security_compute_create_name, security_compute_relabel,
> -security_compute_member, security_compute_user, security_get_initial_context \- query
> +security_compute_member, security_compute_user, security_validatetrans, security_get_initial_context \- query
>  the SELinux policy database in the kernel
>  .
>  .SH "SYNOPSIS"
> @@ -35,6 +35,10 @@ the SELinux policy database in the kernel
>  .sp
>  .BI "int security_compute_user_raw(char *" scon ", const char *" username ", char ***" con );
>  .sp
> +.BI "int security_validatetrans(char *" scon ", const char *" tcon ", security_class_t "tclass ", char *" newcon );
> +.sp
> +.BI "int security_validatetrans_raw(char *" scon ", const char *" tcon ", security_class_t "tclass ", char *" newcon );
> +.sp
>  .BI "int security_get_initial_context(const char *" name ", char **" con );
>  .sp
>  .BI "int security_get_initial_context_raw(const char *" name ", char **" con );
> @@ -100,6 +104,12 @@ is used to determine the set of user contexts that can be reached from a
>  source context. It is mainly used by
>  .BR get_ordered_context_list ().
>
> +.BR security_validatetrans ()
> +is used to determine if a transition from scon to newcon using tcon as the object
> +is valid for object class tclass. This checks against the mlsvalidatetrans and
> +validatetrans constraints in the loaded policy. Returns 0 if allowed, and -1
> +if an error occured with errno set.
> +
>  .BR security_get_initial_context ()
>  is used to get the context of a kernel initial security identifier specified by
>  .I name
> @@ -111,6 +121,7 @@ is used to get the context of a kernel initial security identifier specified by
>  .BR \%security_compute_relabel_raw (),
>  .BR \%security_compute_member_raw (),
>  .BR \%security_compute_user_raw ()
> +.BR \%security_validatetrans_raw ()
>  and
>  .BR \%security_get_initial_context_raw ()
>  behave identically to their non-raw counterparts but do not perform context
> diff --git a/libselinux/man/man3/security_validatetrans.c b/libselinux/man/man3/security_validatetrans.c
> new file mode 100644
> index 00000000..a60bca4d
> --- /dev/null
> +++ b/libselinux/man/man3/security_validatetrans.c
> @@ -0,0 +1 @@
> +.so man3/security_compute_av.3
> diff --git a/libselinux/man/man3/security_validatetrans_raw.c b/libselinux/man/man3/security_validatetrans_raw.c
> new file mode 100644
> index 00000000..a60bca4d
> --- /dev/null
> +++ b/libselinux/man/man3/security_validatetrans_raw.c
> @@ -0,0 +1 @@
> +.so man3/security_compute_av.3
> diff --git a/libselinux/src/selinux_internal.h b/libselinux/src/selinux_internal.h
> index 70b5025d..acd59c7c 100644
> --- a/libselinux/src/selinux_internal.h
> +++ b/libselinux/src/selinux_internal.h
> @@ -29,6 +29,8 @@ hidden_proto(selinux_mkload_policy)
>      hidden_proto(security_compute_create_name_raw)
>      hidden_proto(security_compute_member_raw)
>      hidden_proto(security_compute_relabel_raw)
> +    hidden_proto(security_validatetrans)
> +    hidden_proto(security_validatetrans_raw)
>      hidden_proto(is_selinux_enabled)
>      hidden_proto(is_selinux_mls_enabled)
>      hidden_proto(freecon)
> diff --git a/libselinux/src/validatetrans.c b/libselinux/src/validatetrans.c
> new file mode 100644
> index 00000000..2aa300cf
> --- /dev/null
> +++ b/libselinux/src/validatetrans.c
> @@ -0,0 +1,94 @@
> +#include <unistd.h>
> +#include <sys/types.h>
> +#include <fcntl.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <errno.h>
> +#include <string.h>
> +#include <limits.h>
> +#include "selinux_internal.h"
> +#include "policy.h"
> +#include "mapping.h"
> +
> +int security_validatetrans_raw(const char *scon,
> +                              const char *tcon,
> +                              security_class_t tclass,
> +                              const char *newcon)
> +{
> +       char path[PATH_MAX];
> +       char *buf = NULL;
> +       int size, bufsz;
> +       int fd, ret = -1;
> +       errno = ENOENT;
> +
> +       if (!selinux_mnt) {
> +               return -1;
> +       }
> +
> +       snprintf(path, sizeof path, "%s/validatetrans", selinux_mnt);
> +       fd = open(path, O_WRONLY | O_CLOEXEC);
> +       if (fd < 0) {
> +               return -1;
> +       }
> +
> +       errno = EINVAL;
> +       size = selinux_page_size;
> +       buf = malloc(size);
> +       if (!buf) {
> +               goto out;
> +       }
> +
> +       bufsz = snprintf(buf, size, "%s %s %hu %s", scon, tcon, unmap_class(tclass), newcon);
> +       if (bufsz >= size || bufsz < 0) {
> +               // It got truncated or there was an encoding error
> +               goto out;
> +       }
> +
> +       // clear errno for write()
> +       errno = 0;
> +       ret = write(fd, buf, strlen(buf));
> +       if (ret > 0) {
> +               // The kernel returns the bytes written on success, not 0 as noted in the commit message
> +               ret = 0;
> +       }
> +out:
> +       free(buf);
> +       close(fd);
> +       return ret;
> +}
> +
> +hidden_def(security_validatetrans_raw)
> +
> +int security_validatetrans(const char *scon,
> +                          const char *tcon,
> +                          security_class_t tclass,
> +                          const char *newcon)
> +{
> +       int ret = -1;
> +       char *rscon = NULL;
> +       char *rtcon = NULL;
> +       char *rnewcon = NULL;
> +
> +       if (selinux_trans_to_raw_context(scon, &rscon)) {
> +               goto out;
> +       }
> +
> +       if (selinux_trans_to_raw_context(tcon, &rtcon)) {
> +               goto out;
> +       }
> +
> +       if (selinux_trans_to_raw_context(newcon, &rnewcon)) {
> +               goto out;
> +       }
> +
> +       ret = security_validatetrans_raw(rscon, rtcon, tclass, rnewcon);
> +
> +out:
> +       freecon(rnewcon);
> +       freecon(rtcon);
> +       freecon(rscon);
> +
> +       return ret;
> +}
> +
> +hidden_def(security_validatetrans)
> diff --git a/libselinux/utils/.gitignore b/libselinux/utils/.gitignore
> index 5cd01025..aba18a3c 100644
> --- a/libselinux/utils/.gitignore
> +++ b/libselinux/utils/.gitignore
> @@ -25,3 +25,4 @@ setenforce
>  setfilecon
>  togglesebool
>  selinux_check_access
> +validatetrans
> diff --git a/libselinux/utils/validatetrans.c b/libselinux/utils/validatetrans.c
> new file mode 100644
> index 00000000..1db33e66
> --- /dev/null
> +++ b/libselinux/utils/validatetrans.c
> @@ -0,0 +1,30 @@
> +#include <unistd.h>
> +#include <sys/types.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <selinux/selinux.h>
> +
> +int main(int argc, char **argv)
> +{
> +       security_class_t tclass;
> +       int ret;
> +
> +       if (argc != 5) {
> +               fprintf(stderr, "usage:  %s scontext tcontext tclass newcontext\n",
> +                       argv[0]);
> +               exit(1);
> +       }
> +
> +       tclass = string_to_security_class(argv[3]);
> +       if (!tclass) {
> +               fprintf(stderr, "%s:  invalid class '%s'\n", argv[0], argv[3]);
> +               exit(2);
> +       }
> +
> +       ret = security_validatetrans(argv[1], argv[2], tclass, argv[4]);
> +       printf("security_validatetrans returned %d errno: %s\n", ret, strerror(errno));
> +
> +       return ret;
> +}
> --
> 2.17.2
>

ack - staged: https://github.com/SELinuxProject/selinux/pull/142
Nicolas Iooss April 7, 2019, 8:58 p.m. UTC | #2
On Fri, Apr 5, 2019 at 9:04 PM William Roberts <bill.c.roberts@gmail.com> wrote:
>
> On Fri, Apr 5, 2019 at 12:01 PM Joshua Brindle
> <joshua.brindle@crunchydata.com> wrote:
> >
> > It seems validatetrans support was never added to libselinux, despite being added to
> > selinuxfs in kernel version 4.5
> >
> > There is a utility to test, however the targeted policy has no validatetrans rules so some must be added:
> >
> > $ cat validatetrans.cil
> > (mlsvalidatetrans db_table (and (or (or (or (eq l1 l2) (and (eq t3 unconfined_t) (domby l1 l2))) (and (eq t3 unconfined_t) (dom l1 l2))) (and (eq t3 unconfined_t) (incomp l1 l2))) (or (or (or (eq l1 h2) (and (eq t3 unconfined_t) (domby h1 h2))) (and (eq t3 unconfined_t) (dom h1 h2))) (and (eq t3 unconfined_t) (incomp h1 h2)))))
> >
> > $ sudo semodule -i validatetrans.cil
> >
> > $ ./validatetrans system_u:system_r:kernel_t:s0 system_u:system_r:init_t:s0:c0 db_table system_u:system_r: # invalid context here
> > opening /sys/fs/selinux/validatetrans
> > security_validatetrans returned -1 errno: Invalid argument
> >
> > $ ./validatetrans system_u:system_r:kernel_t:s0 system_u:system_r:init_t:s0:c0 db_table system_u:system_r:init_t:s0
> > opening /sys/fs/selinux/validatetrans
> > security_validatetrans returned -1 errno: Operation not permitted
> >
> > $ ./validatetrans system_u:system_r:kernel_t:s0 system_u:system_r:init_t:s0:c0 db_table system_u:system_r:unconfined_t:s0
> > opening /sys/fs/selinux/validatetrans
> > security_validatetrans returned 0 errno: Success
> >
> > Signed-off-by: Joshua Brindle <joshua.brindle@crunchydata.com>
> > ---
> >  libselinux/include/selinux/selinux.h          | 13 +++
> >  libselinux/man/man3/security_compute_av.3     | 13 ++-
> >  libselinux/man/man3/security_validatetrans.c  |  1 +
> >  .../man/man3/security_validatetrans_raw.c     |  1 +
> >  libselinux/src/selinux_internal.h             |  2 +
> >  libselinux/src/validatetrans.c                | 94 +++++++++++++++++++
> >  libselinux/utils/.gitignore                   |  1 +
> >  libselinux/utils/validatetrans.c              | 30 ++++++
> >  8 files changed, 154 insertions(+), 1 deletion(-)
> >  create mode 100644 libselinux/man/man3/security_validatetrans.c
> >  create mode 100644 libselinux/man/man3/security_validatetrans_raw.c
> >  create mode 100644 libselinux/src/validatetrans.c
> >  create mode 100644 libselinux/utils/validatetrans.c
> >
> > diff --git a/libselinux/include/selinux/selinux.h b/libselinux/include/selinux/selinux.h
> > index a34d54fc..f54f236b 100644
> > --- a/libselinux/include/selinux/selinux.h
> > +++ b/libselinux/include/selinux/selinux.h
> > @@ -255,6 +255,19 @@ extern int security_compute_user_raw(const char * scon,
> >                                      const char *username,
> >                                      char *** con);
> >
> > +/* Validate a transition. This determines whether a transition from scon to newcon
> > +   using tcon as the target for object class tclass is valid in the loaded policy.
> > +   This checks against the mlsvalidatetrans and validatetrans constraints in the loaded policy.
> > +   Returns 0 if allowed and -1 if an error occured with errno set */
> > +extern int security_validatetrans(const char *scon,
> > +                                 const char *tcon,
> > +                                 security_class_t tclass,
> > +                                 const char *newcon);
> > +extern int security_validatetrans_raw(const char *scon,
> > +                                     const char *tcon,
> > +                                     security_class_t tclass,
> > +                                     const char *newcon);
> > +
> >  /* Load a policy configuration. */
> >  extern int security_load_policy(void *data, size_t len);
> >
> > diff --git a/libselinux/man/man3/security_compute_av.3 b/libselinux/man/man3/security_compute_av.3
> > index 2aade5fe..a7181bed 100644
> > --- a/libselinux/man/man3/security_compute_av.3
> > +++ b/libselinux/man/man3/security_compute_av.3
> > @@ -1,7 +1,7 @@
> >  .TH "security_compute_av" "3" "1 January 2004" "russell@coker.com.au" "SELinux API documentation"
> >  .SH "NAME"
> >  security_compute_av, security_compute_av_flags, security_compute_create, security_compute_create_name, security_compute_relabel,
> > -security_compute_member, security_compute_user, security_get_initial_context \- query
> > +security_compute_member, security_compute_user, security_validatetrans, security_get_initial_context \- query
> >  the SELinux policy database in the kernel
> >  .
> >  .SH "SYNOPSIS"
> > @@ -35,6 +35,10 @@ the SELinux policy database in the kernel
> >  .sp
> >  .BI "int security_compute_user_raw(char *" scon ", const char *" username ", char ***" con );
> >  .sp
> > +.BI "int security_validatetrans(char *" scon ", const char *" tcon ", security_class_t "tclass ", char *" newcon );
> > +.sp
> > +.BI "int security_validatetrans_raw(char *" scon ", const char *" tcon ", security_class_t "tclass ", char *" newcon );
> > +.sp
> >  .BI "int security_get_initial_context(const char *" name ", char **" con );
> >  .sp
> >  .BI "int security_get_initial_context_raw(const char *" name ", char **" con );
> > @@ -100,6 +104,12 @@ is used to determine the set of user contexts that can be reached from a
> >  source context. It is mainly used by
> >  .BR get_ordered_context_list ().
> >
> > +.BR security_validatetrans ()
> > +is used to determine if a transition from scon to newcon using tcon as the object
> > +is valid for object class tclass. This checks against the mlsvalidatetrans and
> > +validatetrans constraints in the loaded policy. Returns 0 if allowed, and -1
> > +if an error occured with errno set.
> > +
> >  .BR security_get_initial_context ()
> >  is used to get the context of a kernel initial security identifier specified by
> >  .I name
> > @@ -111,6 +121,7 @@ is used to get the context of a kernel initial security identifier specified by
> >  .BR \%security_compute_relabel_raw (),
> >  .BR \%security_compute_member_raw (),
> >  .BR \%security_compute_user_raw ()
> > +.BR \%security_validatetrans_raw ()
> >  and
> >  .BR \%security_get_initial_context_raw ()
> >  behave identically to their non-raw counterparts but do not perform context
> > diff --git a/libselinux/man/man3/security_validatetrans.c b/libselinux/man/man3/security_validatetrans.c
> > new file mode 100644
> > index 00000000..a60bca4d
> > --- /dev/null
> > +++ b/libselinux/man/man3/security_validatetrans.c
> > @@ -0,0 +1 @@
> > +.so man3/security_compute_av.3
> > diff --git a/libselinux/man/man3/security_validatetrans_raw.c b/libselinux/man/man3/security_validatetrans_raw.c
> > new file mode 100644
> > index 00000000..a60bca4d
> > --- /dev/null
> > +++ b/libselinux/man/man3/security_validatetrans_raw.c
> > @@ -0,0 +1 @@
> > +.so man3/security_compute_av.3
> > diff --git a/libselinux/src/selinux_internal.h b/libselinux/src/selinux_internal.h
> > index 70b5025d..acd59c7c 100644
> > --- a/libselinux/src/selinux_internal.h
> > +++ b/libselinux/src/selinux_internal.h
> > @@ -29,6 +29,8 @@ hidden_proto(selinux_mkload_policy)
> >      hidden_proto(security_compute_create_name_raw)
> >      hidden_proto(security_compute_member_raw)
> >      hidden_proto(security_compute_relabel_raw)
> > +    hidden_proto(security_validatetrans)
> > +    hidden_proto(security_validatetrans_raw)
> >      hidden_proto(is_selinux_enabled)
> >      hidden_proto(is_selinux_mls_enabled)
> >      hidden_proto(freecon)
> > diff --git a/libselinux/src/validatetrans.c b/libselinux/src/validatetrans.c
> > new file mode 100644
> > index 00000000..2aa300cf
> > --- /dev/null
> > +++ b/libselinux/src/validatetrans.c
> > @@ -0,0 +1,94 @@
> > +#include <unistd.h>
> > +#include <sys/types.h>
> > +#include <fcntl.h>
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +#include <errno.h>
> > +#include <string.h>
> > +#include <limits.h>
> > +#include "selinux_internal.h"
> > +#include "policy.h"
> > +#include "mapping.h"
> > +
> > +int security_validatetrans_raw(const char *scon,
> > +                              const char *tcon,
> > +                              security_class_t tclass,
> > +                              const char *newcon)
> > +{
> > +       char path[PATH_MAX];
> > +       char *buf = NULL;
> > +       int size, bufsz;
> > +       int fd, ret = -1;
> > +       errno = ENOENT;
> > +
> > +       if (!selinux_mnt) {
> > +               return -1;
> > +       }
> > +
> > +       snprintf(path, sizeof path, "%s/validatetrans", selinux_mnt);
> > +       fd = open(path, O_WRONLY | O_CLOEXEC);
> > +       if (fd < 0) {
> > +               return -1;
> > +       }
> > +
> > +       errno = EINVAL;
> > +       size = selinux_page_size;
> > +       buf = malloc(size);
> > +       if (!buf) {
> > +               goto out;
> > +       }
> > +
> > +       bufsz = snprintf(buf, size, "%s %s %hu %s", scon, tcon, unmap_class(tclass), newcon);
> > +       if (bufsz >= size || bufsz < 0) {
> > +               // It got truncated or there was an encoding error
> > +               goto out;
> > +       }
> > +
> > +       // clear errno for write()
> > +       errno = 0;
> > +       ret = write(fd, buf, strlen(buf));
> > +       if (ret > 0) {
> > +               // The kernel returns the bytes written on success, not 0 as noted in the commit message
> > +               ret = 0;
> > +       }
> > +out:
> > +       free(buf);
> > +       close(fd);
> > +       return ret;
> > +}
> > +
> > +hidden_def(security_validatetrans_raw)
> > +
> > +int security_validatetrans(const char *scon,
> > +                          const char *tcon,
> > +                          security_class_t tclass,
> > +                          const char *newcon)
> > +{
> > +       int ret = -1;
> > +       char *rscon = NULL;
> > +       char *rtcon = NULL;
> > +       char *rnewcon = NULL;
> > +
> > +       if (selinux_trans_to_raw_context(scon, &rscon)) {
> > +               goto out;
> > +       }
> > +
> > +       if (selinux_trans_to_raw_context(tcon, &rtcon)) {
> > +               goto out;
> > +       }
> > +
> > +       if (selinux_trans_to_raw_context(newcon, &rnewcon)) {
> > +               goto out;
> > +       }
> > +
> > +       ret = security_validatetrans_raw(rscon, rtcon, tclass, rnewcon);
> > +
> > +out:
> > +       freecon(rnewcon);
> > +       freecon(rtcon);
> > +       freecon(rscon);
> > +
> > +       return ret;
> > +}
> > +
> > +hidden_def(security_validatetrans)
> > diff --git a/libselinux/utils/.gitignore b/libselinux/utils/.gitignore
> > index 5cd01025..aba18a3c 100644
> > --- a/libselinux/utils/.gitignore
> > +++ b/libselinux/utils/.gitignore
> > @@ -25,3 +25,4 @@ setenforce
> >  setfilecon
> >  togglesebool
> >  selinux_check_access
> > +validatetrans
> > diff --git a/libselinux/utils/validatetrans.c b/libselinux/utils/validatetrans.c
> > new file mode 100644
> > index 00000000..1db33e66
> > --- /dev/null
> > +++ b/libselinux/utils/validatetrans.c
> > @@ -0,0 +1,30 @@
> > +#include <unistd.h>
> > +#include <sys/types.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <errno.h>
> > +#include <selinux/selinux.h>
> > +
> > +int main(int argc, char **argv)
> > +{
> > +       security_class_t tclass;
> > +       int ret;
> > +
> > +       if (argc != 5) {
> > +               fprintf(stderr, "usage:  %s scontext tcontext tclass newcontext\n",
> > +                       argv[0]);
> > +               exit(1);
> > +       }
> > +
> > +       tclass = string_to_security_class(argv[3]);
> > +       if (!tclass) {
> > +               fprintf(stderr, "%s:  invalid class '%s'\n", argv[0], argv[3]);
> > +               exit(2);
> > +       }
> > +
> > +       ret = security_validatetrans(argv[1], argv[2], tclass, argv[4]);
> > +       printf("security_validatetrans returned %d errno: %s\n", ret, strerror(errno));
> > +
> > +       return ret;
> > +}
> > --
> > 2.17.2
> >
>
> ack - staged: https://github.com/SELinuxProject/selinux/pull/142

Acked-by: Nicolas Iooss <nicolas.iooss@m4x.org>

Thanks!
Nicolas
William Roberts April 9, 2019, 1:51 p.m. UTC | #3
merged:
https://github.com/SELinuxProject/selinux/pull/142

On Sun, Apr 7, 2019 at 1:59 PM Nicolas Iooss <nicolas.iooss@m4x.org> wrote:
>
> On Fri, Apr 5, 2019 at 9:04 PM William Roberts <bill.c.roberts@gmail.com> wrote:
> >
> > On Fri, Apr 5, 2019 at 12:01 PM Joshua Brindle
> > <joshua.brindle@crunchydata.com> wrote:
> > >
> > > It seems validatetrans support was never added to libselinux, despite being added to
> > > selinuxfs in kernel version 4.5
> > >
> > > There is a utility to test, however the targeted policy has no validatetrans rules so some must be added:
> > >
> > > $ cat validatetrans.cil
> > > (mlsvalidatetrans db_table (and (or (or (or (eq l1 l2) (and (eq t3 unconfined_t) (domby l1 l2))) (and (eq t3 unconfined_t) (dom l1 l2))) (and (eq t3 unconfined_t) (incomp l1 l2))) (or (or (or (eq l1 h2) (and (eq t3 unconfined_t) (domby h1 h2))) (and (eq t3 unconfined_t) (dom h1 h2))) (and (eq t3 unconfined_t) (incomp h1 h2)))))
> > >
> > > $ sudo semodule -i validatetrans.cil
> > >
> > > $ ./validatetrans system_u:system_r:kernel_t:s0 system_u:system_r:init_t:s0:c0 db_table system_u:system_r: # invalid context here
> > > opening /sys/fs/selinux/validatetrans
> > > security_validatetrans returned -1 errno: Invalid argument
> > >
> > > $ ./validatetrans system_u:system_r:kernel_t:s0 system_u:system_r:init_t:s0:c0 db_table system_u:system_r:init_t:s0
> > > opening /sys/fs/selinux/validatetrans
> > > security_validatetrans returned -1 errno: Operation not permitted
> > >
> > > $ ./validatetrans system_u:system_r:kernel_t:s0 system_u:system_r:init_t:s0:c0 db_table system_u:system_r:unconfined_t:s0
> > > opening /sys/fs/selinux/validatetrans
> > > security_validatetrans returned 0 errno: Success
> > >
> > > Signed-off-by: Joshua Brindle <joshua.brindle@crunchydata.com>
> > > ---
> > >  libselinux/include/selinux/selinux.h          | 13 +++
> > >  libselinux/man/man3/security_compute_av.3     | 13 ++-
> > >  libselinux/man/man3/security_validatetrans.c  |  1 +
> > >  .../man/man3/security_validatetrans_raw.c     |  1 +
> > >  libselinux/src/selinux_internal.h             |  2 +
> > >  libselinux/src/validatetrans.c                | 94 +++++++++++++++++++
> > >  libselinux/utils/.gitignore                   |  1 +
> > >  libselinux/utils/validatetrans.c              | 30 ++++++
> > >  8 files changed, 154 insertions(+), 1 deletion(-)
> > >  create mode 100644 libselinux/man/man3/security_validatetrans.c
> > >  create mode 100644 libselinux/man/man3/security_validatetrans_raw.c
> > >  create mode 100644 libselinux/src/validatetrans.c
> > >  create mode 100644 libselinux/utils/validatetrans.c
> > >
> > > diff --git a/libselinux/include/selinux/selinux.h b/libselinux/include/selinux/selinux.h
> > > index a34d54fc..f54f236b 100644
> > > --- a/libselinux/include/selinux/selinux.h
> > > +++ b/libselinux/include/selinux/selinux.h
> > > @@ -255,6 +255,19 @@ extern int security_compute_user_raw(const char * scon,
> > >                                      const char *username,
> > >                                      char *** con);
> > >
> > > +/* Validate a transition. This determines whether a transition from scon to newcon
> > > +   using tcon as the target for object class tclass is valid in the loaded policy.
> > > +   This checks against the mlsvalidatetrans and validatetrans constraints in the loaded policy.
> > > +   Returns 0 if allowed and -1 if an error occured with errno set */
> > > +extern int security_validatetrans(const char *scon,
> > > +                                 const char *tcon,
> > > +                                 security_class_t tclass,
> > > +                                 const char *newcon);
> > > +extern int security_validatetrans_raw(const char *scon,
> > > +                                     const char *tcon,
> > > +                                     security_class_t tclass,
> > > +                                     const char *newcon);
> > > +
> > >  /* Load a policy configuration. */
> > >  extern int security_load_policy(void *data, size_t len);
> > >
> > > diff --git a/libselinux/man/man3/security_compute_av.3 b/libselinux/man/man3/security_compute_av.3
> > > index 2aade5fe..a7181bed 100644
> > > --- a/libselinux/man/man3/security_compute_av.3
> > > +++ b/libselinux/man/man3/security_compute_av.3
> > > @@ -1,7 +1,7 @@
> > >  .TH "security_compute_av" "3" "1 January 2004" "russell@coker.com.au" "SELinux API documentation"
> > >  .SH "NAME"
> > >  security_compute_av, security_compute_av_flags, security_compute_create, security_compute_create_name, security_compute_relabel,
> > > -security_compute_member, security_compute_user, security_get_initial_context \- query
> > > +security_compute_member, security_compute_user, security_validatetrans, security_get_initial_context \- query
> > >  the SELinux policy database in the kernel
> > >  .
> > >  .SH "SYNOPSIS"
> > > @@ -35,6 +35,10 @@ the SELinux policy database in the kernel
> > >  .sp
> > >  .BI "int security_compute_user_raw(char *" scon ", const char *" username ", char ***" con );
> > >  .sp
> > > +.BI "int security_validatetrans(char *" scon ", const char *" tcon ", security_class_t "tclass ", char *" newcon );
> > > +.sp
> > > +.BI "int security_validatetrans_raw(char *" scon ", const char *" tcon ", security_class_t "tclass ", char *" newcon );
> > > +.sp
> > >  .BI "int security_get_initial_context(const char *" name ", char **" con );
> > >  .sp
> > >  .BI "int security_get_initial_context_raw(const char *" name ", char **" con );
> > > @@ -100,6 +104,12 @@ is used to determine the set of user contexts that can be reached from a
> > >  source context. It is mainly used by
> > >  .BR get_ordered_context_list ().
> > >
> > > +.BR security_validatetrans ()
> > > +is used to determine if a transition from scon to newcon using tcon as the object
> > > +is valid for object class tclass. This checks against the mlsvalidatetrans and
> > > +validatetrans constraints in the loaded policy. Returns 0 if allowed, and -1
> > > +if an error occured with errno set.
> > > +
> > >  .BR security_get_initial_context ()
> > >  is used to get the context of a kernel initial security identifier specified by
> > >  .I name
> > > @@ -111,6 +121,7 @@ is used to get the context of a kernel initial security identifier specified by
> > >  .BR \%security_compute_relabel_raw (),
> > >  .BR \%security_compute_member_raw (),
> > >  .BR \%security_compute_user_raw ()
> > > +.BR \%security_validatetrans_raw ()
> > >  and
> > >  .BR \%security_get_initial_context_raw ()
> > >  behave identically to their non-raw counterparts but do not perform context
> > > diff --git a/libselinux/man/man3/security_validatetrans.c b/libselinux/man/man3/security_validatetrans.c
> > > new file mode 100644
> > > index 00000000..a60bca4d
> > > --- /dev/null
> > > +++ b/libselinux/man/man3/security_validatetrans.c
> > > @@ -0,0 +1 @@
> > > +.so man3/security_compute_av.3
> > > diff --git a/libselinux/man/man3/security_validatetrans_raw.c b/libselinux/man/man3/security_validatetrans_raw.c
> > > new file mode 100644
> > > index 00000000..a60bca4d
> > > --- /dev/null
> > > +++ b/libselinux/man/man3/security_validatetrans_raw.c
> > > @@ -0,0 +1 @@
> > > +.so man3/security_compute_av.3
> > > diff --git a/libselinux/src/selinux_internal.h b/libselinux/src/selinux_internal.h
> > > index 70b5025d..acd59c7c 100644
> > > --- a/libselinux/src/selinux_internal.h
> > > +++ b/libselinux/src/selinux_internal.h
> > > @@ -29,6 +29,8 @@ hidden_proto(selinux_mkload_policy)
> > >      hidden_proto(security_compute_create_name_raw)
> > >      hidden_proto(security_compute_member_raw)
> > >      hidden_proto(security_compute_relabel_raw)
> > > +    hidden_proto(security_validatetrans)
> > > +    hidden_proto(security_validatetrans_raw)
> > >      hidden_proto(is_selinux_enabled)
> > >      hidden_proto(is_selinux_mls_enabled)
> > >      hidden_proto(freecon)
> > > diff --git a/libselinux/src/validatetrans.c b/libselinux/src/validatetrans.c
> > > new file mode 100644
> > > index 00000000..2aa300cf
> > > --- /dev/null
> > > +++ b/libselinux/src/validatetrans.c
> > > @@ -0,0 +1,94 @@
> > > +#include <unistd.h>
> > > +#include <sys/types.h>
> > > +#include <fcntl.h>
> > > +#include <stdlib.h>
> > > +#include <stdio.h>
> > > +#include <errno.h>
> > > +#include <string.h>
> > > +#include <limits.h>
> > > +#include "selinux_internal.h"
> > > +#include "policy.h"
> > > +#include "mapping.h"
> > > +
> > > +int security_validatetrans_raw(const char *scon,
> > > +                              const char *tcon,
> > > +                              security_class_t tclass,
> > > +                              const char *newcon)
> > > +{
> > > +       char path[PATH_MAX];
> > > +       char *buf = NULL;
> > > +       int size, bufsz;
> > > +       int fd, ret = -1;
> > > +       errno = ENOENT;
> > > +
> > > +       if (!selinux_mnt) {
> > > +               return -1;
> > > +       }
> > > +
> > > +       snprintf(path, sizeof path, "%s/validatetrans", selinux_mnt);
> > > +       fd = open(path, O_WRONLY | O_CLOEXEC);
> > > +       if (fd < 0) {
> > > +               return -1;
> > > +       }
> > > +
> > > +       errno = EINVAL;
> > > +       size = selinux_page_size;
> > > +       buf = malloc(size);
> > > +       if (!buf) {
> > > +               goto out;
> > > +       }
> > > +
> > > +       bufsz = snprintf(buf, size, "%s %s %hu %s", scon, tcon, unmap_class(tclass), newcon);
> > > +       if (bufsz >= size || bufsz < 0) {
> > > +               // It got truncated or there was an encoding error
> > > +               goto out;
> > > +       }
> > > +
> > > +       // clear errno for write()
> > > +       errno = 0;
> > > +       ret = write(fd, buf, strlen(buf));
> > > +       if (ret > 0) {
> > > +               // The kernel returns the bytes written on success, not 0 as noted in the commit message
> > > +               ret = 0;
> > > +       }
> > > +out:
> > > +       free(buf);
> > > +       close(fd);
> > > +       return ret;
> > > +}
> > > +
> > > +hidden_def(security_validatetrans_raw)
> > > +
> > > +int security_validatetrans(const char *scon,
> > > +                          const char *tcon,
> > > +                          security_class_t tclass,
> > > +                          const char *newcon)
> > > +{
> > > +       int ret = -1;
> > > +       char *rscon = NULL;
> > > +       char *rtcon = NULL;
> > > +       char *rnewcon = NULL;
> > > +
> > > +       if (selinux_trans_to_raw_context(scon, &rscon)) {
> > > +               goto out;
> > > +       }
> > > +
> > > +       if (selinux_trans_to_raw_context(tcon, &rtcon)) {
> > > +               goto out;
> > > +       }
> > > +
> > > +       if (selinux_trans_to_raw_context(newcon, &rnewcon)) {
> > > +               goto out;
> > > +       }
> > > +
> > > +       ret = security_validatetrans_raw(rscon, rtcon, tclass, rnewcon);
> > > +
> > > +out:
> > > +       freecon(rnewcon);
> > > +       freecon(rtcon);
> > > +       freecon(rscon);
> > > +
> > > +       return ret;
> > > +}
> > > +
> > > +hidden_def(security_validatetrans)
> > > diff --git a/libselinux/utils/.gitignore b/libselinux/utils/.gitignore
> > > index 5cd01025..aba18a3c 100644
> > > --- a/libselinux/utils/.gitignore
> > > +++ b/libselinux/utils/.gitignore
> > > @@ -25,3 +25,4 @@ setenforce
> > >  setfilecon
> > >  togglesebool
> > >  selinux_check_access
> > > +validatetrans
> > > diff --git a/libselinux/utils/validatetrans.c b/libselinux/utils/validatetrans.c
> > > new file mode 100644
> > > index 00000000..1db33e66
> > > --- /dev/null
> > > +++ b/libselinux/utils/validatetrans.c
> > > @@ -0,0 +1,30 @@
> > > +#include <unistd.h>
> > > +#include <sys/types.h>
> > > +#include <stdio.h>
> > > +#include <stdlib.h>
> > > +#include <string.h>
> > > +#include <errno.h>
> > > +#include <selinux/selinux.h>
> > > +
> > > +int main(int argc, char **argv)
> > > +{
> > > +       security_class_t tclass;
> > > +       int ret;
> > > +
> > > +       if (argc != 5) {
> > > +               fprintf(stderr, "usage:  %s scontext tcontext tclass newcontext\n",
> > > +                       argv[0]);
> > > +               exit(1);
> > > +       }
> > > +
> > > +       tclass = string_to_security_class(argv[3]);
> > > +       if (!tclass) {
> > > +               fprintf(stderr, "%s:  invalid class '%s'\n", argv[0], argv[3]);
> > > +               exit(2);
> > > +       }
> > > +
> > > +       ret = security_validatetrans(argv[1], argv[2], tclass, argv[4]);
> > > +       printf("security_validatetrans returned %d errno: %s\n", ret, strerror(errno));
> > > +
> > > +       return ret;
> > > +}
> > > --
> > > 2.17.2
> > >
> >
> > ack - staged: https://github.com/SELinuxProject/selinux/pull/142
>
> Acked-by: Nicolas Iooss <nicolas.iooss@m4x.org>
>
> Thanks!
> Nicolas
>
diff mbox series

Patch

diff --git a/libselinux/include/selinux/selinux.h b/libselinux/include/selinux/selinux.h
index a34d54fc..f54f236b 100644
--- a/libselinux/include/selinux/selinux.h
+++ b/libselinux/include/selinux/selinux.h
@@ -255,6 +255,19 @@  extern int security_compute_user_raw(const char * scon,
 				     const char *username,
 				     char *** con);
 
+/* Validate a transition. This determines whether a transition from scon to newcon
+   using tcon as the target for object class tclass is valid in the loaded policy.
+   This checks against the mlsvalidatetrans and validatetrans constraints in the loaded policy.
+   Returns 0 if allowed and -1 if an error occured with errno set */
+extern int security_validatetrans(const char *scon,
+				  const char *tcon,
+				  security_class_t tclass,
+				  const char *newcon);
+extern int security_validatetrans_raw(const char *scon,
+				      const char *tcon,
+				      security_class_t tclass,
+				      const char *newcon);
+
 /* Load a policy configuration. */
 extern int security_load_policy(void *data, size_t len);
 
diff --git a/libselinux/man/man3/security_compute_av.3 b/libselinux/man/man3/security_compute_av.3
index 2aade5fe..a7181bed 100644
--- a/libselinux/man/man3/security_compute_av.3
+++ b/libselinux/man/man3/security_compute_av.3
@@ -1,7 +1,7 @@ 
 .TH "security_compute_av" "3" "1 January 2004" "russell@coker.com.au" "SELinux API documentation"
 .SH "NAME"
 security_compute_av, security_compute_av_flags, security_compute_create, security_compute_create_name, security_compute_relabel,
-security_compute_member, security_compute_user, security_get_initial_context \- query
+security_compute_member, security_compute_user, security_validatetrans, security_get_initial_context \- query
 the SELinux policy database in the kernel
 .
 .SH "SYNOPSIS"
@@ -35,6 +35,10 @@  the SELinux policy database in the kernel
 .sp
 .BI "int security_compute_user_raw(char *" scon ", const char *" username ", char ***" con );
 .sp
+.BI "int security_validatetrans(char *" scon ", const char *" tcon ", security_class_t "tclass ", char *" newcon );
+.sp
+.BI "int security_validatetrans_raw(char *" scon ", const char *" tcon ", security_class_t "tclass ", char *" newcon );
+.sp
 .BI "int security_get_initial_context(const char *" name ", char **" con );
 .sp
 .BI "int security_get_initial_context_raw(const char *" name ", char **" con );
@@ -100,6 +104,12 @@  is used to determine the set of user contexts that can be reached from a
 source context. It is mainly used by
 .BR get_ordered_context_list ().
 
+.BR security_validatetrans ()
+is used to determine if a transition from scon to newcon using tcon as the object
+is valid for object class tclass. This checks against the mlsvalidatetrans and
+validatetrans constraints in the loaded policy. Returns 0 if allowed, and -1
+if an error occured with errno set.
+
 .BR security_get_initial_context ()
 is used to get the context of a kernel initial security identifier specified by 
 .I name
@@ -111,6 +121,7 @@  is used to get the context of a kernel initial security identifier specified by
 .BR \%security_compute_relabel_raw (),
 .BR \%security_compute_member_raw (),
 .BR \%security_compute_user_raw ()
+.BR \%security_validatetrans_raw ()
 and
 .BR \%security_get_initial_context_raw ()
 behave identically to their non-raw counterparts but do not perform context
diff --git a/libselinux/man/man3/security_validatetrans.c b/libselinux/man/man3/security_validatetrans.c
new file mode 100644
index 00000000..a60bca4d
--- /dev/null
+++ b/libselinux/man/man3/security_validatetrans.c
@@ -0,0 +1 @@ 
+.so man3/security_compute_av.3
diff --git a/libselinux/man/man3/security_validatetrans_raw.c b/libselinux/man/man3/security_validatetrans_raw.c
new file mode 100644
index 00000000..a60bca4d
--- /dev/null
+++ b/libselinux/man/man3/security_validatetrans_raw.c
@@ -0,0 +1 @@ 
+.so man3/security_compute_av.3
diff --git a/libselinux/src/selinux_internal.h b/libselinux/src/selinux_internal.h
index 70b5025d..acd59c7c 100644
--- a/libselinux/src/selinux_internal.h
+++ b/libselinux/src/selinux_internal.h
@@ -29,6 +29,8 @@  hidden_proto(selinux_mkload_policy)
     hidden_proto(security_compute_create_name_raw)
     hidden_proto(security_compute_member_raw)
     hidden_proto(security_compute_relabel_raw)
+    hidden_proto(security_validatetrans)
+    hidden_proto(security_validatetrans_raw)
     hidden_proto(is_selinux_enabled)
     hidden_proto(is_selinux_mls_enabled)
     hidden_proto(freecon)
diff --git a/libselinux/src/validatetrans.c b/libselinux/src/validatetrans.c
new file mode 100644
index 00000000..2aa300cf
--- /dev/null
+++ b/libselinux/src/validatetrans.c
@@ -0,0 +1,94 @@ 
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include "mapping.h"
+
+int security_validatetrans_raw(const char *scon,
+			       const char *tcon,
+			       security_class_t tclass,
+			       const char *newcon)
+{
+	char path[PATH_MAX];
+	char *buf = NULL;
+	int size, bufsz;
+	int fd, ret = -1;
+	errno = ENOENT;
+
+	if (!selinux_mnt) {
+		return -1;
+	}
+
+	snprintf(path, sizeof path, "%s/validatetrans", selinux_mnt);
+	fd = open(path, O_WRONLY | O_CLOEXEC);
+	if (fd < 0) {
+		return -1;
+	}
+
+	errno = EINVAL;
+	size = selinux_page_size;
+	buf = malloc(size);
+	if (!buf) {
+		goto out;
+	}
+
+	bufsz = snprintf(buf, size, "%s %s %hu %s", scon, tcon, unmap_class(tclass), newcon);
+	if (bufsz >= size || bufsz < 0) {
+		// It got truncated or there was an encoding error
+		goto out;
+	}
+
+	// clear errno for write()
+	errno = 0;
+	ret = write(fd, buf, strlen(buf));
+	if (ret > 0) {
+		// The kernel returns the bytes written on success, not 0 as noted in the commit message
+		ret = 0;
+	}
+out:
+	free(buf);
+	close(fd);
+	return ret;
+}
+
+hidden_def(security_validatetrans_raw)
+
+int security_validatetrans(const char *scon,
+			   const char *tcon,
+			   security_class_t tclass,
+			   const char *newcon)
+{
+	int ret = -1;
+	char *rscon = NULL;
+	char *rtcon = NULL;
+	char *rnewcon = NULL;
+
+	if (selinux_trans_to_raw_context(scon, &rscon)) {
+		goto out;
+	}
+
+	if (selinux_trans_to_raw_context(tcon, &rtcon)) {
+		goto out;
+	}
+
+	if (selinux_trans_to_raw_context(newcon, &rnewcon)) {
+		goto out;
+	}
+
+	ret = security_validatetrans_raw(rscon, rtcon, tclass, rnewcon);
+
+out:
+	freecon(rnewcon);
+	freecon(rtcon);
+	freecon(rscon);
+
+	return ret;
+}
+
+hidden_def(security_validatetrans)
diff --git a/libselinux/utils/.gitignore b/libselinux/utils/.gitignore
index 5cd01025..aba18a3c 100644
--- a/libselinux/utils/.gitignore
+++ b/libselinux/utils/.gitignore
@@ -25,3 +25,4 @@  setenforce
 setfilecon
 togglesebool
 selinux_check_access
+validatetrans
diff --git a/libselinux/utils/validatetrans.c b/libselinux/utils/validatetrans.c
new file mode 100644
index 00000000..1db33e66
--- /dev/null
+++ b/libselinux/utils/validatetrans.c
@@ -0,0 +1,30 @@ 
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <selinux/selinux.h>
+
+int main(int argc, char **argv)
+{
+	security_class_t tclass;
+	int ret;
+
+	if (argc != 5) {
+		fprintf(stderr, "usage:  %s scontext tcontext tclass newcontext\n",
+			argv[0]);
+		exit(1);
+	}
+
+	tclass = string_to_security_class(argv[3]);
+	if (!tclass) {
+		fprintf(stderr, "%s:  invalid class '%s'\n", argv[0], argv[3]);
+		exit(2);
+	}
+
+	ret = security_validatetrans(argv[1], argv[2], tclass, argv[4]);
+	printf("security_validatetrans returned %d errno: %s\n", ret, strerror(errno));
+
+	return ret;
+}