summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Smalley <sds@tycho.nsa.gov>2013-04-29 23:25:17 -0700
committerAndroid Git Automerger <android-git-automerger@android.com>2013-04-29 23:25:17 -0700
commit520c2aaf75887c76631cedf83322cbe4c523d739 (patch)
treee4c0f70423f6d8ed7c96469d96407a75cf3542a1
parent6750780433d7f989b56ac61b655ca982ad9027d4 (diff)
parent4f2b0565ea34081dc2fd04073bb558d6b2609aef (diff)
downloadlibselinux-520c2aaf75887c76631cedf83322cbe4c523d739.tar.gz
am 4f2b0565: Add selinux status functions from upstream libselinux.
* commit '4f2b0565ea34081dc2fd04073bb558d6b2609aef': Add selinux status functions from upstream libselinux.
-rw-r--r--Android.mk4
-rw-r--r--src/deny_unknown.c40
-rw-r--r--src/sestatus.c349
3 files changed, 392 insertions, 1 deletions
diff --git a/Android.mk b/Android.mk
index 14c9aab..6423177 100644
--- a/Android.mk
+++ b/Android.mk
@@ -26,7 +26,9 @@ common_SRC_FILES := \
src/avc_internal.c \
src/avc_sidtab.c \
src/get_initial_context.c \
- src/checkAccess.c
+ src/checkAccess.c \
+ src/sestatus.c \
+ src/deny_unknown.c
common_HOST_FILES := \
src/callbacks.c \
diff --git a/src/deny_unknown.c b/src/deny_unknown.c
new file mode 100644
index 0000000..c93998a
--- /dev/null
+++ b/src/deny_unknown.c
@@ -0,0 +1,40 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include <stdio.h>
+#include <limits.h>
+
+int security_deny_unknown(void)
+{
+ int fd, ret, deny_unknown = 0;
+ char path[PATH_MAX];
+ char buf[20];
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ snprintf(path, sizeof(path), "%s/deny_unknown", selinux_mnt);
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ memset(buf, 0, sizeof(buf));
+ ret = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (ret < 0)
+ return -1;
+
+ if (sscanf(buf, "%d", &deny_unknown) != 1)
+ return -1;
+
+ return deny_unknown;
+}
+
+hidden_def(security_deny_unknown);
diff --git a/src/sestatus.c b/src/sestatus.c
new file mode 100644
index 0000000..ed29dc5
--- /dev/null
+++ b/src/sestatus.c
@@ -0,0 +1,349 @@
+/*
+ * sestatus.c
+ *
+ * APIs to reference SELinux kernel status page (/selinux/status)
+ *
+ * Author: KaiGai Kohei <kaigai@ak.jp.nec.com>
+ *
+ */
+#include <fcntl.h>
+#include <limits.h>
+#include <sched.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "avc_internal.h"
+#include "policy.h"
+
+/*
+ * copied from the selinux/include/security.h
+ */
+struct selinux_status_t
+{
+ uint32_t version; /* version number of thie structure */
+ uint32_t sequence; /* sequence number of seqlock logic */
+ uint32_t enforcing; /* current setting of enforcing mode */
+ uint32_t policyload; /* times of policy reloaded */
+ uint32_t deny_unknown; /* current setting of deny_unknown */
+ /* version > 0 support above status */
+} __attribute((packed));
+
+/*
+ * `selinux_status'
+ *
+ * NULL : not initialized yet
+ * MAP_FAILED : opened, but fallback-mode
+ * Valid Pointer : opened and mapped correctly
+ */
+static struct selinux_status_t *selinux_status = NULL;
+static int selinux_status_fd;
+static uint32_t last_seqno;
+
+static uint32_t fallback_sequence;
+static int fallback_enforcing;
+static int fallback_policyload;
+
+/*
+ * read_sequence
+ *
+ * A utility routine to reference kernel status page according to
+ * seqlock logic. Since selinux_status->sequence is an odd value during
+ * the kernel status page being updated, we try to synchronize completion
+ * of this updating, but we assume it is rare.
+ * The sequence is almost even number.
+ *
+ * __sync_synchronize is a portable memory barrier for various kind
+ * of architecture that is supported by GCC.
+ */
+static inline uint32_t read_sequence(struct selinux_status_t *status)
+{
+ uint32_t seqno = 0;
+
+ do {
+ /*
+ * No need for sched_yield() in the first trial of
+ * this loop.
+ */
+ if (seqno & 0x0001)
+ sched_yield();
+
+ seqno = status->sequence;
+
+ __sync_synchronize();
+
+ } while (seqno & 0x0001);
+
+ return seqno;
+}
+
+/*
+ * selinux_status_updated
+ *
+ * It returns whether something has been happened since the last call.
+ * Because `selinux_status->sequence' shall be always incremented on
+ * both of setenforce/policyreload events, so differences from the last
+ * value informs us something has been happened.
+ */
+int selinux_status_updated(void)
+{
+ uint32_t curr_seqno;
+ int result = 0;
+
+ if (selinux_status == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (selinux_status == MAP_FAILED) {
+ if (avc_netlink_check_nb() < 0)
+ return -1;
+
+ curr_seqno = fallback_sequence;
+ } else {
+ curr_seqno = read_sequence(selinux_status);
+ }
+
+ /*
+ * `curr_seqno' is always even-number, so it does not match with
+ * `last_seqno' being initialized to odd-number in the first call.
+ * We never return 'something was updated' in the first call,
+ * because this function focuses on status-updating since the last
+ * invocation.
+ */
+ if (last_seqno & 0x0001)
+ last_seqno = curr_seqno;
+
+ if (last_seqno != curr_seqno)
+ {
+ last_seqno = curr_seqno;
+ result = 1;
+ }
+ return result;
+}
+
+/*
+ * selinux_status_getenforce
+ *
+ * It returns the current performing mode of SELinux.
+ * 1 means currently we run in enforcing mode, or 0 means permissive mode.
+ */
+int selinux_status_getenforce(void)
+{
+ uint32_t seqno;
+ uint32_t enforcing;
+
+ if (selinux_status == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (selinux_status == MAP_FAILED) {
+ if (avc_netlink_check_nb() < 0)
+ return -1;
+
+ return fallback_enforcing;
+ }
+
+ /* sequence must not be changed during references */
+ do {
+ seqno = read_sequence(selinux_status);
+
+ enforcing = selinux_status->enforcing;
+
+ } while (seqno != read_sequence(selinux_status));
+
+ return enforcing ? 1 : 0;
+}
+
+/*
+ * selinux_status_policyload
+ *
+ * It returns times of policy reloaded on the running system.
+ * Note that it is not a reliable value on fallback-mode until it receives
+ * the first event message via netlink socket, so, a correct usage of this
+ * value is to compare it with the previous value to detect policy reloaded
+ * event.
+ */
+int selinux_status_policyload(void)
+{
+ uint32_t seqno;
+ uint32_t policyload;
+
+ if (selinux_status == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (selinux_status == MAP_FAILED) {
+ if (avc_netlink_check_nb() < 0)
+ return -1;
+
+ return fallback_policyload;
+ }
+
+ /* sequence must not be changed during references */
+ do {
+ seqno = read_sequence(selinux_status);
+
+ policyload = selinux_status->policyload;
+
+ } while (seqno != read_sequence(selinux_status));
+
+ return policyload;
+}
+
+/*
+ * selinux_status_deny_unknown
+ *
+ * It returns a guideline to handle undefined object classes or permissions.
+ * 0 means SELinux treats policy queries on undefined stuff being allowed,
+ * however, 1 means such queries are denied.
+ */
+int selinux_status_deny_unknown(void)
+{
+ uint32_t seqno;
+ uint32_t deny_unknown;
+
+ if (selinux_status == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (selinux_status == MAP_FAILED)
+ return security_deny_unknown();
+
+ /* sequence must not be changed during references */
+ do {
+ seqno = read_sequence(selinux_status);
+
+ deny_unknown = selinux_status->deny_unknown;
+
+ } while (seqno != read_sequence(selinux_status));
+
+ return deny_unknown ? 1 : 0;
+}
+
+/*
+ * callback routines for fallback case using netlink socket
+ */
+static int fallback_cb_setenforce(int enforcing)
+{
+ fallback_sequence += 2;
+ fallback_enforcing = enforcing;
+
+ return 0;
+}
+
+static int fallback_cb_policyload(int policyload)
+{
+ fallback_sequence += 2;
+ fallback_policyload = policyload;
+
+ return 0;
+}
+
+/*
+ * selinux_status_open
+ *
+ * It tries to open and mmap kernel status page (/selinux/status).
+ * Since Linux 2.6.37 or later supports this feature, we may run
+ * fallback routine using a netlink socket on older kernels, if
+ * the supplied `fallback' is not zero.
+ * It returns 0 on success, or -1 on error.
+ */
+int selinux_status_open(int fallback)
+{
+ int fd;
+ char path[PATH_MAX];
+ long pagesize;
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ pagesize = sysconf(_SC_PAGESIZE);
+ if (pagesize < 0)
+ return -1;
+
+ snprintf(path, sizeof(path), "%s/status", selinux_mnt);
+ fd = open(path, O_RDONLY | O_CLOEXEC);
+ if (fd < 0)
+ goto error;
+
+ selinux_status = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd, 0);
+ if (selinux_status == MAP_FAILED) {
+ close(fd);
+ goto error;
+ }
+ selinux_status_fd = fd;
+ last_seqno = (uint32_t)(-1);
+
+ return 0;
+
+error:
+ /*
+ * If caller wants fallback routine, we try to provide
+ * an equivalent functionality using existing netlink
+ * socket, although it needs system call invocation to
+ * receive event notification.
+ */
+ if (fallback && avc_netlink_open(0) == 0) {
+ union selinux_callback cb;
+
+ /* register my callbacks */
+ cb.func_setenforce = fallback_cb_setenforce;
+ selinux_set_callback(SELINUX_CB_SETENFORCE, cb);
+ cb.func_policyload = fallback_cb_policyload;
+ selinux_set_callback(SELINUX_CB_POLICYLOAD, cb);
+
+ /* mark as fallback mode */
+ selinux_status = MAP_FAILED;
+ selinux_status_fd = avc_netlink_acquire_fd();
+ last_seqno = (uint32_t)(-1);
+
+ fallback_sequence = 0;
+ fallback_enforcing = security_getenforce();
+ fallback_policyload = 0;
+
+ return 1;
+ }
+ selinux_status = NULL;
+
+ return -1;
+}
+
+/*
+ * selinux_status_close
+ *
+ * It unmap and close the kernel status page, or close netlink socket
+ * if fallback mode.
+ */
+void selinux_status_close(void)
+{
+ long pagesize;
+
+ /* not opened */
+ if (selinux_status == NULL)
+ return;
+
+ /* fallback-mode */
+ if (selinux_status == MAP_FAILED)
+ {
+ avc_netlink_release_fd();
+ avc_netlink_close();
+ selinux_status = NULL;
+ return;
+ }
+
+ pagesize = sysconf(_SC_PAGESIZE);
+ /* not much we can do other than leak memory */
+ if (pagesize > 0)
+ munmap(selinux_status, pagesize);
+ selinux_status = NULL;
+
+ close(selinux_status_fd);
+ selinux_status_fd = -1;
+ last_seqno = (uint32_t)(-1);
+}