Add function selinux_check_access_flags() that is the same as
selinux_check_access() except that it will also return the avd
flags. Currently only the SELINUX_AVD_FLAGS_PERMISSIVE flag is
used to signify that the source type is defined as permissive
in policy.

Because selinux_check_access_flags() can return before the AVC
call, and also the AVC call may return with undefined avd flags,
the returned flags should be checked for SELINUX_AVD_FLAGS_UNDEFINED,
first. If set, the remaining flags are undefined. See the
selinux_check_access_flags() man page entry for details. There is a
utility for testing the functionality:
utils/selinux_check_access -f scon tcon class perm

As a consequence of implementing selinux_check_access_flags, additional
calls have been added to avc.c: avc_has_perm_flags() and
avc_has_perm_noaudit_flags(). The appropriate man pages have been updated.

There is a utility for testing the avc_has_perm() and avc_has_perm_flags():
utils/avc_has_perm -f scon tcon class perm

Signed-off-by: Richard Haines <[email protected]>
---
 libselinux/include/selinux/avc.h                 |  68 +++++++
 libselinux/include/selinux/selinux.h             |  32 +++
 libselinux/man/man3/avc_has_perm.3               |  37 +++-
 libselinux/man/man3/security_compute_av.3        |  21 +-
 libselinux/man/man3/selinux_check_access_flags.3 |   1 +
 libselinux/src/avc.c                             |  44 ++++-
 libselinux/src/avc_internal.h                    |   1 +
 libselinux/src/checkAccess.c                     |  63 +++---
 libselinux/utils/.gitignore                      |   2 +
 libselinux/utils/avc_has_perm.c                  | 235 +++++++++++++++++++++++
 libselinux/utils/selinux_check_access.c          | 189 ++++++++++++++++++
 11 files changed, 660 insertions(+), 33 deletions(-)
 create mode 100644 libselinux/man/man3/selinux_check_access_flags.3
 create mode 100644 libselinux/utils/avc_has_perm.c
 create mode 100644 libselinux/utils/selinux_check_access.c

diff --git a/libselinux/include/selinux/avc.h b/libselinux/include/selinux/avc.h
index b4bc6f3..89d75c3 100644
--- a/libselinux/include/selinux/avc.h
+++ b/libselinux/include/selinux/avc.h
@@ -264,6 +264,43 @@ int avc_has_perm_noaudit(security_id_t ssid,
                         struct avc_entry_ref *aeref, struct av_decision *avd);
 
 /**
+ * avc_has_perm_noaudit_flags - Check permissions but perform no auditing,
+ *                              return avd flags.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions, interpreted based on @tclass
+ * @aeref:  AVC entry reference
+ * @avd: access vector decisions
+ * @flags: returned avd flags. Currently two flags are supported:
+ *         SELINUX_AVD_FLAGS_UNDEFINED This is typically set when the
+ *         source or target context is not valid in policy, or the
+ *         avc is in permissive mode, or the returned entry could not be
+ *         inserted into the avc cache.
+ *         SELINUX_AVD_FLAGS_PERMISSIVE, which indicates the decision is
+ *         computed on a policy defined permissive domain.
+ *
+ * Check the AVC to determine whether the @requested permissions are granted
+ * for the SID pair (@ssid, @tsid), interpreting the permissions
+ * based on @tclass, and call the security server on a cache miss to obtain
+ * a new decision and add it to the cache.  Update @aeref to refer to an AVC
+ * entry with the resulting decisions, and return a copy of the decisions
+ * in @avd.  Return %0 if all @requested permissions are granted, -%1 with
+ * @errno set to %EACCES if any permissions are denied, or to another value
+ * upon other errors.  This function is typically called by avc_has_perm(),
+ * but may also be called directly to separate permission checking from
+ * auditing, e.g. in cases where a lock must be held for the check but
+ * should be released for the auditing.
+ */
+int avc_has_perm_noaudit_flags(security_id_t ssid,
+                              security_id_t tsid,
+                              security_class_t tclass,
+                              access_vector_t requested,
+                              struct avc_entry_ref *aeref,
+                              struct av_decision *avd,
+                              unsigned int *flags);
+
+/**
  * avc_has_perm - Check permissions and perform any appropriate auditing.
  * @ssid: source security identifier
  * @tsid: target security identifier
@@ -286,6 +323,37 @@ int avc_has_perm(security_id_t ssid, security_id_t tsid,
                 struct avc_entry_ref *aeref, void *auditdata);
 
 /**
+ * avc_has_perm_flags - Check permissions, returning avd flags and perform any
+ *                      appropriate auditing.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions, interpreted based on @tclass
+ * @aeref:  AVC entry reference
+ * @auditdata: auxiliary audit data
+ * @flags: returned avd flags. Currently two flags are supported:
+ *         SELINUX_AVD_FLAGS_UNDEFINED This is typically caused when the
+ *         source or target context is not valid in policy, or the
+ *         avc is in permissive mode, or the returned entry could not be
+ *         inserted into the avc cache.
+ *         SELINUX_AVD_FLAGS_PERMISSIVE, which indicates the decision is
+ *         computed on a policy defined permissive domain.
+ *
+ * Check the AVC to determine whether the @requested permissions are granted
+ * for the SID pair (@ssid, @tsid), interpreting the permissions
+ * based on @tclass, and call the security server on a cache miss to obtain
+ * a new decision and add it to the cache.  Update @aeref to refer to an AVC
+ * entry with the resulting decisions.  Audit the granting or denial of
+ * permissions in accordance with the policy.  Return %0 if all @requested
+ * permissions are granted, -%1 with @errno set to %EACCES if any permissions
+ * are denied or to another value upon other errors.
+ */
+int avc_has_perm_flags(security_id_t ssid, security_id_t tsid,
+                     security_class_t tclass, access_vector_t requested,
+                     struct avc_entry_ref *aeref, void *auditdata,
+                     unsigned int *flags);
+
+/**
  * avc_audit - Audit the granting or denial of permissions.
  * @ssid: source security identifier
  * @tsid: target security identifier
diff --git a/libselinux/include/selinux/selinux.h 
b/libselinux/include/selinux/selinux.h
index 45dd6ca..4e9d209 100644
--- a/libselinux/include/selinux/selinux.h
+++ b/libselinux/include/selinux/selinux.h
@@ -136,6 +136,7 @@ struct av_decision {
 
 /* Definitions of av_decision.flags */
 #define SELINUX_AVD_FLAGS_PERMISSIVE   0x0001
+#define SELINUX_AVD_FLAGS_UNDEFINED    0x8000
 
 /* Structure for passing options, used by AVC and label subsystems */
 struct selinux_opt {
@@ -578,6 +579,37 @@ extern const char *selinux_path(void);
  */
 extern int selinux_check_access(const char * scon, const char * tcon, const 
char *tclass, const char *perm, void *auditdata);
 
+/**
+ * selinux_check_access_flags - Check permissions, returning avd flags and
+ * perform any appropriate auditing.
+ * @scon: source security context
+ * @tcon: target security context
+ * @tclass: target security class string
+ * @perm: requested permissions string, interpreted based on @tclass
+ * @auditdata: auxiliary audit data
+ * @flags: returned avd flags. Currently two flags are supported:
+ *         SELINUX_AVD_FLAGS_UNDEFINED, which indicates that the remaining
+ *         @flags should not be checked as the avd flags could not be read.
+ *         SELINUX_AVD_FLAGS_PERMISSIVE, which indicates the decision is
+ *         computed on a policy defined permissive domain.
+ *
+ * Check the AVC to determine whether the @perm permissions are granted
+ * for the SID pair (@scon, @tcon), interpreting the permissions
+ * based on @tclass.
+ * Return %0 if all @perm permissions are granted, -%1 with
+ * @errno set to %EACCES if any permissions are denied or to another
+ * value upon other errors.
+ * If auditing or logging is configured the appropriate callbacks will be
+ * called and passed the auditdata field.
+ * If selinux_check_access_flags() fails before calling the AVC, then @flags
+ * will be returned with a value of SELINUX_AVD_FLAGS_UNDEFINED as the avd
+ * flags could not be read. This will happen when either SELinux is disabled
+ * or there is an unknown class/permission.
+ */
+extern int selinux_check_access_flags(const char *scon, const char *tcon,
+                                     const char *tclass, const char *perm,
+                                     void *auditdata, unsigned int *flags);
+
 /* Check a permission in the passwd class.
    Return 0 if granted or -1 otherwise. */
 extern int selinux_check_passwd_access(access_vector_t requested);
diff --git a/libselinux/man/man3/avc_has_perm.3 
b/libselinux/man/man3/avc_has_perm.3
index 3e9fca8..74d3420 100644
--- a/libselinux/man/man3/avc_has_perm.3
+++ b/libselinux/man/man3/avc_has_perm.3
@@ -19,6 +19,13 @@ avc_has_perm, avc_has_perm_noaudit, avc_audit, 
avc_entry_ref_init \- obtain and
 .BI "struct avc_entry_ref *" aeref ", void *" auditdata ");"
 .in
 .sp
+.BI "int avc_has_perm_flags(security_id_t " ssid ", security_id_t " tsid ,
+.in +\w'int avc_has_perm('u
+.BI "security_class_t " tclass ", access_vector_t " requested ,
+.br
+.BI "struct avc_entry_ref *" aeref ", void *" auditdata ", int *" flags ");"
+.in
+.sp
 .BI "int avc_has_perm_noaudit(security_id_t " ssid ", security_id_t " tsid ,
 .in +\w'int avc_has_perm('u
 .BI "security_class_t " tclass ", access_vector_t " requested ,
@@ -60,6 +67,21 @@ parameter is for supplemental auditing; see
 .BR avc_audit ()
 below.
 
+.BR avc_has_perm_flags ()
+is identical to
+.BR avc_has_perm ()
+but additionally sets the
+.I flags
+field. On return
+.I flags
+must be tested and if
+.BR SELINUX_AVD_FLAGS_UNDEFINED ,
+then the remaining
+.I flags
+should not be checked. Currently one other flag is supported:
+.BR SELINUX_AVD_FLAGS_PERMISSIVE ,
+which indicates the decision is computed on a policy defined permissive domain.
+
 .BR avc_has_perm_noaudit ()
 behaves as
 .BR avc_has_perm ()
@@ -69,6 +91,15 @@ and can be passed to
 .BR avc_audit ()
 explicitly.
 
+.BR avc_has_perm_noaudit_flags ()
+behaves as
+.BR avc_has_perm_flags ()
+without producing an audit message.  The access decision is returned in
+.I avd
+and can be passed to
+.BR avc_audit ()
+explicitly.
+
 .BR avc_audit ()
 produces an audit message for the access query represented by
 .IR ssid ,
@@ -101,9 +132,11 @@ After declaring an
 structure, use
 .BR avc_entry_ref_init ()
 to initialize it before passing it to
-.BR avc_has_perm ()
+.BR avc_has_perm (),
+.BR avc_has_perm_flags (),
+.BR avc_has_perm_noaudit ()
 or
-.BR \%avc_has_perm_noaudit ()
+.BR avc_has_perm_noaudit_flags ()
 for the first time.
 Using an uninitialized structure will produce undefined behavior.
 .
diff --git a/libselinux/man/man3/security_compute_av.3 
b/libselinux/man/man3/security_compute_av.3
index 2aade5f..2a9312f 100644
--- a/libselinux/man/man3/security_compute_av.3
+++ b/libselinux/man/man3/security_compute_av.3
@@ -39,7 +39,9 @@ the SELinux policy database in the kernel
 .sp
 .BI "int security_get_initial_context_raw(const char *" name ", char **" con );
 .sp
-.BI "int selinux_check_access(const char *" scon ", const char *" tcon ", 
const char *" class ", const char *" perm ", void *" auditdata);
+.BI "int selinux_check_access(const char *" scon ", const char *" tcon ", 
const char *" class ", const char *" perm ", void *" auditdata );
+.sp
+.BI "int selinux_check_access_flags(const char *" scon ", const char *" tcon 
", const char *" class ", const char *" perm ", void *" auditdata ", int *" 
flags );
 .sp
 .BI "int selinux_check_passwd_access(access_vector_t " requested );
 .sp
@@ -67,7 +69,7 @@ field of
 .IR avd .
 Currently one flag is supported:
 .BR SELINUX_AVD_FLAGS_PERMISSIVE ,
-which indicates the decision is computed on a permissive domain.
+which indicates the decision is computed on a policy defined permissive domain.
 
 .BR security_compute_create ()
 is used to compute a context to use for labeling a new object in a particular
@@ -119,6 +121,21 @@ translation.
 .BR selinux_check_access ()
 is used to check if the source context has the access permission for the 
specified class on the target context.
 
+.BR selinux_check_access_flags ()
+is identical to
+.BR selinux_check_access ()
+but additionally sets the
+.I flags
+field. On return
+.I flags
+must be tested and if
+.BR SELINUX_AVD_FLAGS_UNDEFINED ,
+then the remaining
+.I flags
+should not be checked. Currently one other flag is supported:
+.BR SELINUX_AVD_FLAGS_PERMISSIVE ,
+which indicates the decision is computed on a policy defined permissive domain.
+
 .BR selinux_check_passwd_access ()
 is used to check for a permission in the
 .I passwd
diff --git a/libselinux/man/man3/selinux_check_access_flags.3 
b/libselinux/man/man3/selinux_check_access_flags.3
new file mode 100644
index 0000000..a60bca4
--- /dev/null
+++ b/libselinux/man/man3/selinux_check_access_flags.3
@@ -0,0 +1 @@
+.so man3/security_compute_av.3
diff --git a/libselinux/src/avc.c b/libselinux/src/avc.c
index b1ec57f..87f4c76 100644
--- a/libselinux/src/avc.c
+++ b/libselinux/src/avc.c
@@ -741,12 +741,27 @@ static void avd_init(struct av_decision *avd)
        avd->flags = 0;
 }
 
+
 int avc_has_perm_noaudit(security_id_t ssid,
                         security_id_t tsid,
                         security_class_t tclass,
                         access_vector_t requested,
                         struct avc_entry_ref *aeref, struct av_decision *avd)
 {
+       return avc_has_perm_noaudit_flags(ssid, tsid, tclass,
+                                         requested, aeref, avd, NULL);
+}
+
+hidden_def(avc_has_perm_noaudit)
+
+int avc_has_perm_noaudit_flags(security_id_t ssid,
+                              security_id_t tsid,
+                              security_class_t tclass,
+                              access_vector_t requested,
+                              struct avc_entry_ref *aeref,
+                              struct av_decision *avd,
+                              unsigned int *flags)
+{
        struct avc_entry *ae;
        int rc = 0;
        struct avc_entry entry;
@@ -790,13 +805,13 @@ int avc_has_perm_noaudit(security_id_t ssid,
                                                           &entry.avd);
                        if (rc && errno == EINVAL && !avc_enforcing) {
                                rc = errno = 0;
-                               goto out;
+                               goto set_undef;
                        }
                        if (rc)
-                               goto out;
+                               goto set_undef;
                        rc = avc_insert(ssid, tsid, tclass, &entry, aeref);
                        if (rc)
-                               goto out;
+                               goto set_undef;
                }
                ae = aeref->ae;
        }
@@ -816,21 +831,38 @@ int avc_has_perm_noaudit(security_id_t ssid,
                }
        }
 
-      out:
+       if (flags)
+               *flags = ae->avd.flags;
+out:
        avc_release_lock(avc_lock);
        return rc;
+
+set_undef:
+       if (flags)
+               *flags = SELINUX_AVD_FLAGS_UNDEFINED;
+       goto out;
 }
 
-hidden_def(avc_has_perm_noaudit)
+hidden_def(avc_has_perm_noaudit_flags)
 
 int avc_has_perm(security_id_t ssid, security_id_t tsid,
                 security_class_t tclass, access_vector_t requested,
                 struct avc_entry_ref *aeref, void *auditdata)
 {
+       return avc_has_perm_flags(ssid, tsid, tclass, requested, aeref,
+                                 auditdata, NULL);
+}
+
+int avc_has_perm_flags(security_id_t ssid, security_id_t tsid,
+                      security_class_t tclass, access_vector_t requested,
+                      struct avc_entry_ref *aeref, void *auditdata,
+                      unsigned int *flags)
+{
        struct av_decision avd;
        int errsave, rc;
 
-       rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, aeref, &avd);
+       rc = avc_has_perm_noaudit_flags(ssid, tsid, tclass,
+                                 requested, aeref, &avd, flags);
        errsave = errno;
        avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
        errno = errsave;
diff --git a/libselinux/src/avc_internal.h b/libselinux/src/avc_internal.h
index f851659..82781d1 100644
--- a/libselinux/src/avc_internal.h
+++ b/libselinux/src/avc_internal.h
@@ -179,4 +179,5 @@ hidden_proto(avc_av_stats)
     hidden_proto(avc_reset)
     hidden_proto(avc_audit)
     hidden_proto(avc_has_perm_noaudit)
+    hidden_proto(avc_has_perm_noaudit_flags)
 #endif                         /* _SELINUX_AVC_INTERNAL_H_ */
diff --git a/libselinux/src/checkAccess.c b/libselinux/src/checkAccess.c
index 8de5747..0a3d79b 100644
--- a/libselinux/src/checkAccess.c
+++ b/libselinux/src/checkAccess.c
@@ -1,4 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
 #include <unistd.h>
 #include <sys/types.h>
 #include <stdlib.h>
@@ -32,7 +31,16 @@ static void avc_init_once(void)
        }
 }
 
-int selinux_check_access(const char *scon, const char *tcon, const char 
*class, const char *perm, void *aux) {
+int selinux_check_access(const char *scon, const char *tcon, const char *class,
+                        const char *perm, void *aux)
+{
+       return selinux_check_access_flags(scon, tcon, class, perm, aux, NULL);
+}
+
+int selinux_check_access_flags(const char *scon, const char *tcon,
+                              const char *class, const char *perm,
+                              void *aux, unsigned int *flags)
+{
        int rc;
        security_id_t scon_id;
        security_id_t tcon_id;
@@ -41,6 +49,13 @@ int selinux_check_access(const char *scon, const char *tcon, 
const char *class,
 
        __selinux_once(once, avc_init_once);
 
+       /* Set flags undefined as avc_has_perm_flags may never get called,
+        * as either SELinux is disabled or there is an unknown
+        * class/permission.
+        */
+       if (flags)
+               *flags = SELINUX_AVD_FLAGS_UNDEFINED;
+
        if (selinux_enabled != 1)
                return 0;
 
@@ -54,27 +69,29 @@ int selinux_check_access(const char *scon, const char 
*tcon, const char *class,
 
        (void) avc_netlink_check_nb();
 
-       sclass = string_to_security_class(class);
-       if (sclass == 0) {
-              rc = errno;
-              avc_log(SELINUX_ERROR, "Unknown class %s", class);
-              if (security_deny_unknown() == 0)
-                      return 0;
-              errno = rc;
-              return -1;
-       }
-
-       av = string_to_av_perm(sclass, perm);
-       if (av == 0) {
-              rc = errno;
-              avc_log(SELINUX_ERROR, "Unknown permission %s for class %s", 
perm, class);
-              if (security_deny_unknown() == 0)
-                      return 0;
-              errno = rc;
-              return -1;
-       }
-
-       return avc_has_perm (scon_id, tcon_id, sclass, av, NULL, aux);
+       sclass = string_to_security_class(class);
+       if (sclass == 0) {
+               rc = errno;
+               avc_log(SELINUX_ERROR, "Unknown class %s", class);
+               if (security_deny_unknown() == 0)
+                       return 0;
+               errno = rc;
+               return -1;
+       }
+
+       av = string_to_av_perm(sclass, perm);
+       if (av == 0) {
+               rc = errno;
+               avc_log(SELINUX_ERROR, "Unknown permission %s for class %s",
+                       perm, class);
+               if (security_deny_unknown() == 0)
+                       return 0;
+               errno = rc;
+               return -1;
+       }
+
+       return avc_has_perm_flags(scon_id, tcon_id, sclass, av, NULL,
+                                 aux, flags);
 }
 
 int selinux_check_passwd_access(access_vector_t requested)
diff --git a/libselinux/utils/.gitignore b/libselinux/utils/.gitignore
index ed3bf0b..07c0870 100644
--- a/libselinux/utils/.gitignore
+++ b/libselinux/utils/.gitignore
@@ -25,3 +25,5 @@ selinuxexeccon
 setenforce
 setfilecon
 togglesebool
+avc_has_perm
+selinux_check_access
diff --git a/libselinux/utils/avc_has_perm.c b/libselinux/utils/avc_has_perm.c
new file mode 100644
index 0000000..c144674
--- /dev/null
+++ b/libselinux/utils/avc_has_perm.c
@@ -0,0 +1,235 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#include <selinux/avc.h>
+
+static void usage(char *progname)
+{
+       fprintf(stderr, "usage:  %s [-f] [-i] [-p] scon tcon class perm\n"
+               "\nWhere:\n\t"
+               "-f  Call avc_has_perm_flags(3) to obtain the avd\n\t"
+               "    flags. Currently only the SELINUX_AVD_FLAGS_PERMISSIVE\n\t"
+               "    flag is defined. If set, signifies the source type is\n\t"
+               "    defined in policy as a PERMISSIVE type.\n\t"
+               "    The default is to call avc_has_perm(3) that\n\t"
+               "    does not request the avd flags.\n\t"
+               "-i  Interactive mode. Once displayed first result, can\n\t"
+               "    enter additional entries and display AVC cache info.\n"
+               "-p  Set avc_open to permissive mode.\n",
+               progname);
+       exit(1);
+}
+
+static void get_entry(char **buffer)
+{
+       char *buf;
+       int len;
+#define BUF_LEN 81
+
+       buf = malloc(BUF_LEN * sizeof(char));
+       if (!buf) {
+               perror("malloc");
+               exit(1);
+       }
+
+       if (fgets(buf, BUF_LEN - 1, stdin) == NULL) {
+               perror("fgets");
+               exit(1);
+       }
+
+       len = strlen(buf);
+       if (buf[len - 1] == '\n')
+               buf[len - 1] = 0;
+
+       *buffer = buf;
+}
+
+/*
+ * Function to print the AVC statistics. Because no audit logging call back
+ * has been set, the avc_cache_stats will be displayed on stderr.
+ */
+static void print_avc_stats(void)
+{
+       struct avc_cache_stats acs;
+
+       avc_cache_stats(&acs);
+       printf("\nThe avc_cache_stats are as follows:\n");
+       printf("entry_hits:     %d\t(Decisions found in aeref)\n",
+              acs.entry_hits);
+       printf("entry_misses:   %d\t(Decisions not found in aeref)\n",
+              acs.entry_misses);
+       printf("entry_discards: %d\t(Decisions not found in aeref that were "
+              "also non-NULL)\n", acs.entry_discards);
+       printf("entry_lookups:  %d\t(Queries made)\n", acs.entry_lookups);
+       printf("cav_lookups:    %d\t(Cache lookups)\n", acs.cav_lookups);
+       printf("cav_hits:       %d\t(Cache hits)\n", acs.cav_hits);
+       printf("cav_probes:     %d\t(Entries examined searching the cache)\n",
+              acs.cav_probes);
+       printf("cav_misses:     %d\t(Cache misses)\n\n", acs.cav_misses);
+}
+
+struct avc_entry_ref aeref;
+static void exec_func(char *scon, char *tcon, char *class, char *perm,
+                     bool get_flags)
+{
+       int rc;
+       unsigned int flags;
+       context_t context;
+       const char *type;
+       security_id_t scon_id;
+       security_id_t tcon_id;
+       security_class_t sclass;
+       access_vector_t av;
+
+       rc = avc_context_to_sid(scon, &scon_id);
+       if (rc < 0) {
+               perror("Error scon avc_context_to_sid");
+               exit(1);
+       }
+
+       rc = avc_context_to_sid(tcon, &tcon_id);
+       if (rc < 0) {
+               perror("Error tcon avc_context_to_sid");
+               exit(1);
+       }
+
+       sclass = string_to_security_class(class);
+       av = string_to_av_perm(sclass, perm);
+
+       context = context_new(scon);
+       if (!context) {
+               perror("Error context_new");
+               exit(1);
+       }
+       type = context_type_get(context);
+       if (!type) {
+               perror("Error context_type_get");
+               free(context);
+               exit(1);
+       }
+
+       printf("\nAny avc_log error messages are shown on stderr:\n");
+       if (get_flags)
+               rc = avc_has_perm_flags(scon_id, tcon_id, sclass, av, &aeref,
+                                       NULL, &flags);
+       else
+               rc = avc_has_perm(scon_id, tcon_id, sclass, av, &aeref, NULL);
+       printf("\nEnd of avc_log error messages.\n\n");
+
+       if (rc < 0) {
+               printf("Error %s: %s\n",
+                      get_flags ? "avc_has_perm_flags" : "avc_has_perm",
+                      strerror(errno));
+       } else {
+               printf("Permission ALLOWED.\n");
+       }
+
+       if (get_flags) {
+               if (flags & SELINUX_AVD_FLAGS_UNDEFINED)
+                       printf("AVD flags are undefined.\n");
+               else if (flags & SELINUX_AVD_FLAGS_PERMISSIVE)
+                       printf("%s is defined in policy as a PERMISSIVE "
+                              "domain.\n", type);
+               else
+                       printf("%s is NOT defined in policy as a PERMISSIVE "
+                              "domain.\n", type);
+       }
+       context_free(context);
+}
+
+int main(int argc, char **argv)
+{
+       int opt, rc;
+       bool get_flags = false, interactive = false;
+       char *scon, *tcon, *class, *perm;
+       struct selinux_opt avc_option;
+
+       avc_option.type = AVC_OPT_SETENFORCE;
+       avc_option.value = (char *)1;
+
+       while ((opt = getopt(argc, argv, "fip")) != -1) {
+               switch (opt) {
+               case 'f':
+                       get_flags = true;
+                       break;
+               case 'i':
+                       interactive = true;
+                       break;
+               case 'p':
+                       avc_option.value = NULL;
+                       break;
+               default:
+                       usage(argv[0]);
+               }
+       }
+
+       if ((argc - optind) != 4)
+               usage(argv[0]);
+
+       rc = is_selinux_enabled();
+       if (rc == 0) {
+               printf("SELinux is not enabled.\n");
+               exit(1);
+       } else if (rc == 1) {
+               printf("SELinux is enabled.\n");
+       } else {
+               perror("Error is_selinux_enabled");
+               exit(1);
+       }
+
+       rc = security_getenforce();
+       if (rc == 0)
+               printf("SELinux running in PERMISSIVE mode.\n");
+       else if (rc == 1)
+               printf("SELinux running in ENFORCING mode.\n");
+       else {
+               perror("Error security_getenforce");
+               exit(1);
+       }
+
+       rc = security_deny_unknown();
+       if (rc == 0)
+               printf("Undefined object classes or permissions: ALLOWED.\n");
+       else if (rc == 1)
+               printf("Undefined object classes or permissions: DENIED.\n");
+       else {
+               perror("Error security_deny_unknown");
+               exit(1);
+       }
+
+       if (avc_open(&avc_option, 1)) {
+               perror("Error avc_open");
+               exit(1);
+       }
+
+       if (avc_option.value == NULL)
+               printf("avc_open - PERMISSIVE mode.\n");
+       else
+               printf("avc_open - ENFORCING mode.\n");
+
+       avc_entry_ref_init(&aeref);
+
+       exec_func(argv[optind], argv[optind + 1], argv[optind + 2],
+                 argv[optind + 3], get_flags);
+
+       while (interactive) {
+               printf("\nEnter scon: ");
+               get_entry(&scon);
+               printf("Enter tcon: ");
+               get_entry(&tcon);
+               printf("Enter class: ");
+               get_entry(&class);
+               printf("Enter perm: ");
+               get_entry(&perm);
+
+               exec_func(scon, tcon, class, perm, get_flags);
+               print_avc_stats();
+       }
+
+       exit(0);
+}
diff --git a/libselinux/utils/selinux_check_access.c 
b/libselinux/utils/selinux_check_access.c
new file mode 100644
index 0000000..06cbaf5
--- /dev/null
+++ b/libselinux/utils/selinux_check_access.c
@@ -0,0 +1,189 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#include <selinux/avc.h>
+
+static void usage(char *progname)
+{
+       fprintf(stderr, "usage:  %s [-f] [-i] scon tcon class perm\n"
+               "\nWhere:\n\t"
+               "-f  Call selinux_check_access_flags(3) to obtain the avd\n\t"
+               "    flags. Currently only the SELINUX_AVD_FLAGS_PERMISSIVE\n\t"
+               "    flag is defined. If set, signifies the source type is\n\t"
+               "    defined in policy as a PERMISSIVE type.\n\t"
+               "    The default is to call selinux_check_access(3) that\n\t"
+               "    does not request the avd flags.\n\t"
+               "-i  Interactive mode. Once displayed first result, can\n\t"
+               "    enter additional entries and display AVC cache info.\n",
+               progname);
+       exit(1);
+}
+
+static void get_entry(char **buffer)
+{
+       char *buf;
+       int len;
+#define BUF_LEN 81
+
+       buf = malloc(BUF_LEN * sizeof(char));
+       if (!buf) {
+               perror("malloc");
+               exit(1);
+       }
+
+       if (fgets(buf, BUF_LEN - 1, stdin) == NULL) {
+               perror("fgets");
+               exit(1);
+       }
+
+       len = strlen(buf);
+       if (buf[len - 1] == '\n')
+               buf[len - 1] = 0;
+
+       *buffer = buf;
+}
+
+/*
+ * Function to print the AVC statistics. Because no audit logging call back
+ * has been set, the avc_cache_stats will be displayed on stderr.
+ * selinux_check_access* sets aeref = NULL, so do not print these stats.
+ */
+static void print_avc_stats(void)
+{
+       struct avc_cache_stats acs;
+
+       avc_cache_stats(&acs);
+       printf("\nThe avc_cache_stats are as follows:\n");
+       printf("entry_lookups:  %d\t(Queries made)\n", acs.entry_lookups);
+       printf("cav_lookups:    %d\t(Cache lookups)\n", acs.cav_lookups);
+       printf("cav_hits:       %d\t(Cache hits)\n", acs.cav_hits);
+       printf("cav_probes:     %d\t(Entries examined searching the cache)\n",
+              acs.cav_probes);
+       printf("cav_misses:     %d\t(Cache misses)\n\n", acs.cav_misses);
+}
+
+static void exec_func(char *scon, char *tcon, char *class, char *perm,
+                     bool get_flags)
+{
+       int rc;
+       unsigned int flags;
+       context_t context;
+       const char *type;
+
+       context = context_new(scon);
+       if (!context) {
+               perror("Error context_new");
+               exit(1);
+       }
+       type = context_type_get(context);
+       if (!type) {
+               perror("Error context_type_get");
+               free(context);
+               exit(1);
+       }
+
+       printf("\nAny avc_log error messages are shown on stderr:\n");
+       if (get_flags)
+               rc = selinux_check_access_flags(scon, tcon, class, perm,
+                                               NULL, &flags);
+       else
+               rc = selinux_check_access(scon, tcon, class, perm, NULL);
+       printf("\nEnd of avc_log error messages.\n\n");
+
+       if (rc < 0)
+               printf("Error %s: %s\n",
+                      get_flags ? "selinux_check_access_flags" :
+                      "selinux_check_access", strerror(errno));
+       else
+               printf("Permission ALLOWED.\n");
+
+       if (get_flags) {
+               if (flags & SELINUX_AVD_FLAGS_UNDEFINED)
+                       printf("AVD flags are undefined.\n");
+               else if (flags & SELINUX_AVD_FLAGS_PERMISSIVE)
+                       printf("%s is defined in policy as a PERMISSIVE "
+                              "domain.\n", type);
+               else
+                       printf("%s is NOT defined in policy as a PERMISSIVE "
+                              "domain.\n", type);
+       }
+       context_free(context);
+}
+
+int main(int argc, char **argv)
+{
+       int opt, rc;
+       bool get_flags = false, interactive = false;
+       char *scon, *tcon, *class, *perm;
+
+       while ((opt = getopt(argc, argv, "fi")) != -1) {
+               switch (opt) {
+               case 'f':
+                       get_flags = true;
+                       break;
+               case 'i':
+                       interactive = true;
+                       break;
+               default:
+                       usage(argv[0]);
+               }
+       }
+
+       if ((argc - optind) != 4)
+               usage(argv[0]);
+
+       rc = is_selinux_enabled();
+       if (rc == 0) {
+               printf("SELinux is not enabled.\n");
+               exit(1);
+       } else if (rc == 1) {
+               printf("SELinux is enabled.\n");
+       } else {
+               perror("Error is_selinux_enabled");
+               exit(1);
+       }
+
+       rc = security_getenforce();
+       if (rc == 0)
+               printf("SELinux running in PERMISSIVE mode.\n");
+       else if (rc == 1)
+               printf("SELinux running in ENFORCING mode.\n");
+       else {
+               perror("Error security_getenforce");
+               exit(1);
+       }
+
+       rc = security_deny_unknown();
+       if (rc == 0)
+               printf("Undefined object classes or permissions: ALLOWED.\n");
+       else if (rc == 1)
+               printf("Undefined object classes or permissions: DENIED.\n");
+       else {
+               perror("Error security_deny_unknown");
+               exit(1);
+       }
+
+       exec_func(argv[optind], argv[optind + 1], argv[optind + 2],
+                 argv[optind + 3], get_flags);
+
+       while (interactive) {
+               printf("\nEnter scon: ");
+               get_entry(&scon);
+               printf("Enter tcon: ");
+               get_entry(&tcon);
+               printf("Enter class: ");
+               get_entry(&class);
+               printf("Enter perm: ");
+               get_entry(&perm);
+
+               exec_func(scon, tcon, class, perm, get_flags);
+               print_avc_stats();
+       }
+
+       exit(0);
+}
-- 
2.9.3

Reply via email to