aboutsummaryrefslogtreecommitdiffstats
path: root/libselinux/src/stringrep.c
diff options
context:
space:
mode:
Diffstat (limited to 'libselinux/src/stringrep.c')
-rw-r--r--libselinux/src/stringrep.c576
1 files changed, 576 insertions, 0 deletions
diff --git a/libselinux/src/stringrep.c b/libselinux/src/stringrep.c
new file mode 100644
index 00000000..19c5bb6c
--- /dev/null
+++ b/libselinux/src/stringrep.c
@@ -0,0 +1,576 @@
+/*
+ * String representation support for classes and permissions.
+ */
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <selinux/flask.h>
+#include <selinux/av_permissions.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include "mapping.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+/* The following code looks complicated, but it really is not. What it
+ does is to generate two variables. The first is basically a struct
+ of arrays. The second is the real array of structures which would
+ have used string pointers. But instead it now uses an offset value
+ into the first structure. Strings are accessed indirectly by an
+ explicit addition of the string index and the base address of the
+ structure with the strings (all type safe). The advantage is that
+ there are no relocations necessary in the array with the data as it
+ would be the case with string pointers. This has advantages at
+ load time, the data section is smaller, and it is read-only. */
+#define L1(line) L2(line)
+#define L2(line) str##line
+static const union av_perm_to_string_data {
+ struct {
+#define S_(c, v, s) char L1(__LINE__)[sizeof(s)];
+#include "av_perm_to_string.h"
+#undef S_
+ };
+ char str[0];
+} av_perm_to_string_data = {
+ {
+#define S_(c, v, s) s,
+#include "av_perm_to_string.h"
+#undef S_
+ }
+};
+static const struct av_perm_to_string {
+ uint16_t tclass;
+ uint16_t nameidx;
+ uint32_t value;
+} av_perm_to_string[] = {
+#define S_(c, v, s) { c, offsetof(union av_perm_to_string_data, L1(__LINE__)), v },
+#include "av_perm_to_string.h"
+#undef S_
+};
+
+#undef L1
+#undef L2
+
+#define L1(line) L2(line)
+#define L2(line) str##line
+static const union class_to_string_data {
+ struct {
+#define S_(s) char L1(__LINE__)[sizeof(s)];
+#include "class_to_string.h"
+#undef S_
+ };
+ char str[0];
+} class_to_string_data = {
+ {
+#define S_(s) s,
+#include "class_to_string.h"
+#undef S_
+ }
+};
+static const uint16_t class_to_string[] = {
+#define S_(s) offsetof(union class_to_string_data, L1(__LINE__)),
+#include "class_to_string.h"
+#undef S_
+};
+
+#undef L1
+#undef L2
+
+static const union common_perm_to_string_data {
+ struct {
+#define L1(line) L2(line)
+#define L2(line) str##line
+#define S_(s) char L1(__LINE__)[sizeof(s)];
+#define TB_(s)
+#define TE_(s)
+#include "common_perm_to_string.h"
+#undef S_
+#undef L1
+#undef L2
+ };
+ char str[0];
+} common_perm_to_string_data = {
+ {
+#define S_(s) s,
+#include "common_perm_to_string.h"
+#undef S_
+#undef TB_
+#undef TE_
+ }
+};
+static const union common_perm_to_string {
+ struct {
+#define TB_(s) struct {
+#define TE_(s) } s##_part;
+#define S_(s) uint16_t L1(__LINE__)
+#define L1(l) L2(l)
+#define L2(l) field_##l;
+#include "common_perm_to_string.h"
+#undef TB_
+#undef TE_
+#undef S_
+#undef L1
+#undef L2
+ };
+ uint16_t data[0];
+} common_perm_to_string = {
+ {
+#define TB_(s) {
+#define TE_(s) },
+#define S_(s) offsetof(union common_perm_to_string_data, L1(__LINE__)),
+#define L1(line) L2(line)
+#define L2(line) str##line
+#include "common_perm_to_string.h"
+#undef TB_
+#undef TE_
+#undef S_
+#undef L1
+#undef L2
+ }
+};
+
+static const struct av_inherit {
+ uint16_t tclass;
+ uint16_t common_pts_idx;
+ uint32_t common_base;
+} av_inherit[] = {
+#define S_(c, i, b) { c, offsetof(union common_perm_to_string, common_##i##_perm_to_string_part)/sizeof(uint16_t), b },
+#include "av_inherit.h"
+#undef S_
+};
+
+#define NCLASSES ARRAY_SIZE(class_to_string)
+#define NVECTORS ARRAY_SIZE(av_perm_to_string)
+#define MAXVECTORS 8*sizeof(access_vector_t)
+
+extern int obj_class_compat;
+
+struct discover_class_node {
+ char *name;
+ security_class_t value;
+ char **perms;
+
+ struct discover_class_node *next;
+};
+
+static struct discover_class_node *discover_class_cache = NULL;
+
+static struct discover_class_node * get_class_cache_entry_name(const char *s)
+{
+ struct discover_class_node *node = discover_class_cache;
+
+ for (; node != NULL && strcmp(s,node->name) != 0; node = node->next);
+
+ return node;
+}
+
+static struct discover_class_node * get_class_cache_entry_value(security_class_t c)
+{
+ struct discover_class_node *node = discover_class_cache;
+
+ for (; node != NULL && c != node->value; node = node->next);
+
+ return node;
+}
+
+static struct discover_class_node * discover_class(const char *s)
+{
+ int fd, ret;
+ char path[PATH_MAX];
+ char buf[20];
+ DIR *dir;
+ struct dirent *dentry;
+ size_t i;
+
+ struct discover_class_node *node;
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ /* allocate a node */
+ node = malloc(sizeof(struct discover_class_node));
+ if (node == NULL)
+ return NULL;
+
+ /* allocate array for perms */
+ node->perms = calloc(NVECTORS,sizeof(char*));
+ if (node->perms == NULL)
+ goto err1;
+
+ /* load up the name */
+ node->name = strdup(s);
+ if (node->name == NULL)
+ goto err2;
+
+ /* load up class index */
+ snprintf(path, sizeof path, "%s/class/%s/index", selinux_mnt,s);
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ goto err3;
+
+ memset(buf, 0, sizeof(buf));
+ ret = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (ret < 0)
+ goto err3;
+
+ if (sscanf(buf, "%u", (unsigned int*)&node->value) != 1)
+ goto err3;
+
+ /* load up permission indicies */
+ snprintf(path, sizeof path, "%s/class/%s/perms",selinux_mnt,s);
+ dir = opendir(path);
+ if (dir == NULL)
+ goto err3;
+
+ dentry = readdir(dir);
+ while (dentry != NULL) {
+ unsigned int value;
+ struct stat m;
+
+ snprintf(path, sizeof path, "%s/class/%s/perms/%s", selinux_mnt,s,dentry->d_name);
+ if (stat(path,&m) < 0)
+ goto err4;
+
+ if (m.st_mode & S_IFDIR) {
+ dentry = readdir(dir);
+ continue;
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ goto err4;
+
+ memset(buf, 0, sizeof(buf));
+ ret = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (ret < 0)
+ goto err4;
+
+ if (sscanf(buf, "%u", &value) != 1)
+ goto err4;
+
+ node->perms[value-1] = strdup(dentry->d_name);
+ if (node->perms[value-1] == NULL)
+ goto err4;
+
+ dentry = readdir(dir);
+ }
+ closedir(dir);
+
+ node->next = discover_class_cache;
+ discover_class_cache = node;
+
+ return node;
+
+err4:
+ closedir(dir);
+ for (i=0; i<NVECTORS; i++)
+ free(node->perms[i]);
+err3:
+ free(node->name);
+err2:
+ free(node->perms);
+err1:
+ free(node);
+ return NULL;
+}
+
+void flush_class_cache(void)
+{
+ struct discover_class_node *cur = discover_class_cache, *prev = NULL;
+ size_t i;
+
+ while (cur != NULL) {
+ free(cur->name);
+
+ for (i=0 ; i<MAXVECTORS ; i++)
+ free(cur->perms[i]);
+
+ free(cur->perms);
+
+ prev = cur;
+ cur = cur->next;
+
+ free(prev);
+ }
+
+ discover_class_cache = NULL;
+}
+
+static security_class_t string_to_security_class_compat(const char *s)
+{
+ unsigned int val;
+
+ if (isdigit(s[0])) {
+ val = atoi(s);
+ if (val > 0 && val < NCLASSES)
+ return map_class(val);
+ } else {
+ for (val = 0; val < NCLASSES; val++) {
+ if (strcmp(s, (class_to_string_data.str
+ + class_to_string[val])) == 0)
+ return map_class(val);
+ }
+ }
+
+ errno = EINVAL;
+ return 0;
+}
+
+static access_vector_t string_to_av_perm_compat(security_class_t kclass, const char *s)
+{
+ const uint16_t *common_pts_idx = 0;
+ access_vector_t perm, common_base = 0;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(av_inherit); i++) {
+ if (av_inherit[i].tclass == kclass) {
+ common_pts_idx =
+ &common_perm_to_string.data[av_inherit[i].
+ common_pts_idx];
+ common_base = av_inherit[i].common_base;
+ break;
+ }
+ }
+
+ i = 0;
+ perm = 1;
+ while (perm < common_base) {
+ if (strcmp
+ (s,
+ common_perm_to_string_data.str + common_pts_idx[i]) == 0)
+ return perm;
+ perm <<= 1;
+ i++;
+ }
+
+ for (i = 0; i < NVECTORS; i++) {
+ if ((av_perm_to_string[i].tclass == kclass) &&
+ (strcmp(s, (av_perm_to_string_data.str
+ + av_perm_to_string[i].nameidx)) == 0))
+ return av_perm_to_string[i].value;
+ }
+
+ errno = EINVAL;
+ return 0;
+}
+
+static const char *security_class_to_string_compat(security_class_t tclass)
+{
+ if (tclass > 0 && tclass < NCLASSES)
+ return class_to_string_data.str + class_to_string[tclass];
+
+ errno = EINVAL;
+ return NULL;
+}
+
+static const char *security_av_perm_to_string_compat(security_class_t tclass,
+ access_vector_t av)
+{
+ const uint16_t *common_pts_idx = 0;
+ access_vector_t common_base = 0;
+ unsigned int i;
+
+ if (!av)
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(av_inherit); i++) {
+ if (av_inherit[i].tclass == tclass) {
+ common_pts_idx =
+ &common_perm_to_string.data[av_inherit[i].
+ common_pts_idx];
+ common_base = av_inherit[i].common_base;
+ break;
+ }
+ }
+
+ if (av < common_base) {
+ i = 0;
+ while (!(av & 1)) {
+ av >>= 1;
+ i++;
+ }
+ return common_perm_to_string_data.str + common_pts_idx[i];
+ }
+
+ for (i = 0; i < NVECTORS; i++) {
+ if (av_perm_to_string[i].tclass == tclass &&
+ av_perm_to_string[i].value == av)
+ return av_perm_to_string_data.str
+ + av_perm_to_string[i].nameidx;
+ }
+
+ errno = EINVAL;
+ return NULL;
+}
+
+security_class_t string_to_security_class(const char *s)
+{
+ struct discover_class_node *node;
+
+ if (obj_class_compat)
+ return string_to_security_class_compat(s);
+
+ node = get_class_cache_entry_name(s);
+ if (node == NULL) {
+ node = discover_class(s);
+
+ if (node == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ }
+
+ return map_class(node->value);
+}
+
+access_vector_t string_to_av_perm(security_class_t tclass, const char *s)
+{
+ struct discover_class_node *node;
+ security_class_t kclass = unmap_class(tclass);
+
+ if (obj_class_compat)
+ return map_perm(tclass, string_to_av_perm_compat(kclass, s));
+
+ node = get_class_cache_entry_value(kclass);
+ if (node != NULL) {
+ size_t i;
+ for (i=0; i<MAXVECTORS && node->perms[i] != NULL; i++)
+ if (strcmp(node->perms[i],s) == 0)
+ return map_perm(tclass, 1<<i);
+ }
+
+ errno = EINVAL;
+ return 0;
+}
+
+const char *security_class_to_string(security_class_t tclass)
+{
+ struct discover_class_node *node;
+
+ tclass = unmap_class(tclass);
+
+ if (obj_class_compat)
+ return security_class_to_string_compat(tclass);
+
+ node = get_class_cache_entry_value(tclass);
+ if (node == NULL)
+ return security_class_to_string_compat(tclass);
+ else
+ return node->name;
+}
+
+const char *security_av_perm_to_string(security_class_t tclass,
+ access_vector_t av)
+{
+ struct discover_class_node *node;
+ size_t i;
+
+ av = unmap_perm(tclass, av);
+ tclass = unmap_class(tclass);
+
+ if (obj_class_compat)
+ return security_av_perm_to_string_compat(tclass,av);
+
+ node = get_class_cache_entry_value(tclass);
+ if (av && node)
+ for (i = 0; i<MAXVECTORS; i++)
+ if ((1<<i) & av)
+ return node->perms[i];
+
+ return security_av_perm_to_string_compat(tclass,av);
+}
+
+int security_av_string(security_class_t tclass, access_vector_t av, char **res)
+{
+ unsigned int i = 0;
+ size_t len = 5;
+ access_vector_t tmp = av;
+ int rc = 0;
+ const char *str;
+ char *ptr;
+
+ /* first pass computes the required length */
+ while (tmp) {
+ if (tmp & 1) {
+ str = security_av_perm_to_string(tclass, av & (1<<i));
+ if (str)
+ len += strlen(str) + 1;
+ else {
+ rc = -1;
+ errno = EINVAL;
+ goto out;
+ }
+ }
+ tmp >>= 1;
+ i++;
+ }
+
+ *res = malloc(len);
+ if (!*res) {
+ rc = -1;
+ goto out;
+ }
+
+ /* second pass constructs the string */
+ i = 0;
+ tmp = av;
+ ptr = *res;
+
+ if (!av) {
+ sprintf(ptr, "null");
+ goto out;
+ }
+
+ ptr += sprintf(ptr, "{ ");
+ while (tmp) {
+ if (tmp & 1)
+ ptr += sprintf(ptr, "%s ", security_av_perm_to_string(
+ tclass, av & (1<<i)));
+ tmp >>= 1;
+ i++;
+ }
+ sprintf(ptr, "}");
+out:
+ return rc;
+}
+
+void print_access_vector(security_class_t tclass, access_vector_t av)
+{
+ const char *permstr;
+ access_vector_t bit = 1;
+
+ if (av == 0) {
+ printf(" null");
+ return;
+ }
+
+ printf(" {");
+
+ while (av) {
+ if (av & bit) {
+ permstr = security_av_perm_to_string(tclass, bit);
+ if (!permstr)
+ break;
+ printf(" %s", permstr);
+ av &= ~bit;
+ }
+ bit <<= 1;
+ }
+
+ if (av)
+ printf(" 0x%x", av);
+ printf(" }");
+}