aboutsummaryrefslogtreecommitdiff
path: root/security/selinux/ss/policydb.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/selinux/ss/policydb.c')
-rw-r--r--security/selinux/ss/policydb.c205
1 files changed, 191 insertions, 14 deletions
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 26646305dc0..72e4a54973a 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -30,6 +30,7 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/errno.h>
+#include <linux/audit.h>
#include "security.h"
#include "policydb.h"
@@ -116,7 +117,12 @@ static struct policydb_compat_info policydb_compat[] = {
.version = POLICYDB_VERSION_PERMISSIVE,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
- }
+ },
+ {
+ .version = POLICYDB_VERSION_BOUNDARY,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
};
static struct policydb_compat_info *policydb_lookup_compat(int version)
@@ -254,7 +260,9 @@ static int role_index(void *key, void *datum, void *datap)
role = datum;
p = datap;
- if (!role->value || role->value > p->p_roles.nprim)
+ if (!role->value
+ || role->value > p->p_roles.nprim
+ || role->bounds > p->p_roles.nprim)
return -EINVAL;
p->p_role_val_to_name[role->value - 1] = key;
p->role_val_to_struct[role->value - 1] = role;
@@ -270,9 +278,12 @@ static int type_index(void *key, void *datum, void *datap)
p = datap;
if (typdatum->primary) {
- if (!typdatum->value || typdatum->value > p->p_types.nprim)
+ if (!typdatum->value
+ || typdatum->value > p->p_types.nprim
+ || typdatum->bounds > p->p_types.nprim)
return -EINVAL;
p->p_type_val_to_name[typdatum->value - 1] = key;
+ p->type_val_to_struct[typdatum->value - 1] = typdatum;
}
return 0;
@@ -285,7 +296,9 @@ static int user_index(void *key, void *datum, void *datap)
usrdatum = datum;
p = datap;
- if (!usrdatum->value || usrdatum->value > p->p_users.nprim)
+ if (!usrdatum->value
+ || usrdatum->value > p->p_users.nprim
+ || usrdatum->bounds > p->p_users.nprim)
return -EINVAL;
p->p_user_val_to_name[usrdatum->value - 1] = key;
p->user_val_to_struct[usrdatum->value - 1] = usrdatum;
@@ -438,6 +451,14 @@ static int policydb_index_others(struct policydb *p)
goto out;
}
+ p->type_val_to_struct =
+ kmalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)),
+ GFP_KERNEL);
+ if (!p->type_val_to_struct) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
if (cond_init_bool_indexes(p)) {
rc = -ENOMEM;
goto out;
@@ -625,6 +646,7 @@ void policydb_destroy(struct policydb *p)
kfree(p->class_val_to_struct);
kfree(p->role_val_to_struct);
kfree(p->user_val_to_struct);
+ kfree(p->type_val_to_struct);
avtab_destroy(&p->te_avtab);
@@ -1176,8 +1198,8 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = NULL;
struct role_datum *role;
- int rc;
- __le32 buf[2];
+ int rc, to_read = 2;
+ __le32 buf[3];
u32 len;
role = kzalloc(sizeof(*role), GFP_KERNEL);
@@ -1186,12 +1208,17 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp)
goto out;
}
- rc = next_entry(buf, fp, sizeof buf);
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+ to_read = 3;
+
+ rc = next_entry(buf, fp, sizeof(buf[0]) * to_read);
if (rc < 0)
goto bad;
len = le32_to_cpu(buf[0]);
role->value = le32_to_cpu(buf[1]);
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+ role->bounds = le32_to_cpu(buf[2]);
key = kmalloc(len + 1, GFP_KERNEL);
if (!key) {
@@ -1236,8 +1263,8 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = NULL;
struct type_datum *typdatum;
- int rc;
- __le32 buf[3];
+ int rc, to_read = 3;
+ __le32 buf[4];
u32 len;
typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL);
@@ -1246,13 +1273,27 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp)
return rc;
}
- rc = next_entry(buf, fp, sizeof buf);
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+ to_read = 4;
+
+ rc = next_entry(buf, fp, sizeof(buf[0]) * to_read);
if (rc < 0)
goto bad;
len = le32_to_cpu(buf[0]);
typdatum->value = le32_to_cpu(buf[1]);
- typdatum->primary = le32_to_cpu(buf[2]);
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) {
+ u32 prop = le32_to_cpu(buf[2]);
+
+ if (prop & TYPEDATUM_PROPERTY_PRIMARY)
+ typdatum->primary = 1;
+ if (prop & TYPEDATUM_PROPERTY_ATTRIBUTE)
+ typdatum->attribute = 1;
+
+ typdatum->bounds = le32_to_cpu(buf[3]);
+ } else {
+ typdatum->primary = le32_to_cpu(buf[2]);
+ }
key = kmalloc(len + 1, GFP_KERNEL);
if (!key) {
@@ -1309,8 +1350,8 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = NULL;
struct user_datum *usrdatum;
- int rc;
- __le32 buf[2];
+ int rc, to_read = 2;
+ __le32 buf[3];
u32 len;
usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL);
@@ -1319,12 +1360,17 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp)
goto out;
}
- rc = next_entry(buf, fp, sizeof buf);
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+ to_read = 3;
+
+ rc = next_entry(buf, fp, sizeof(buf[0]) * to_read);
if (rc < 0)
goto bad;
len = le32_to_cpu(buf[0]);
usrdatum->value = le32_to_cpu(buf[1]);
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+ usrdatum->bounds = le32_to_cpu(buf[2]);
key = kmalloc(len + 1, GFP_KERNEL);
if (!key) {
@@ -1465,6 +1511,133 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp)
cat_read,
};
+static int user_bounds_sanity_check(void *key, void *datum, void *datap)
+{
+ struct user_datum *upper, *user;
+ struct policydb *p = datap;
+ int depth = 0;
+
+ upper = user = datum;
+ while (upper->bounds) {
+ struct ebitmap_node *node;
+ unsigned long bit;
+
+ if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
+ printk(KERN_ERR "SELinux: user %s: "
+ "too deep or looped boundary",
+ (char *) key);
+ return -EINVAL;
+ }
+
+ upper = p->user_val_to_struct[upper->bounds - 1];
+ ebitmap_for_each_positive_bit(&user->roles, node, bit) {
+ if (ebitmap_get_bit(&upper->roles, bit))
+ continue;
+
+ printk(KERN_ERR
+ "SELinux: boundary violated policy: "
+ "user=%s role=%s bounds=%s\n",
+ p->p_user_val_to_name[user->value - 1],
+ p->p_role_val_to_name[bit],
+ p->p_user_val_to_name[upper->value - 1]);
+
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int role_bounds_sanity_check(void *key, void *datum, void *datap)
+{
+ struct role_datum *upper, *role;
+ struct policydb *p = datap;
+ int depth = 0;
+
+ upper = role = datum;
+ while (upper->bounds) {
+ struct ebitmap_node *node;
+ unsigned long bit;
+
+ if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
+ printk(KERN_ERR "SELinux: role %s: "
+ "too deep or looped bounds\n",
+ (char *) key);
+ return -EINVAL;
+ }
+
+ upper = p->role_val_to_struct[upper->bounds - 1];
+ ebitmap_for_each_positive_bit(&role->types, node, bit) {
+ if (ebitmap_get_bit(&upper->types, bit))
+ continue;
+
+ printk(KERN_ERR
+ "SELinux: boundary violated policy: "
+ "role=%s type=%s bounds=%s\n",
+ p->p_role_val_to_name[role->value - 1],
+ p->p_type_val_to_name[bit],
+ p->p_role_val_to_name[upper->value - 1]);
+
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int type_bounds_sanity_check(void *key, void *datum, void *datap)
+{
+ struct type_datum *upper, *type;
+ struct policydb *p = datap;
+ int depth = 0;
+
+ upper = type = datum;
+ while (upper->bounds) {
+ if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
+ printk(KERN_ERR "SELinux: type %s: "
+ "too deep or looped boundary\n",
+ (char *) key);
+ return -EINVAL;
+ }
+
+ upper = p->type_val_to_struct[upper->bounds - 1];
+ if (upper->attribute) {
+ printk(KERN_ERR "SELinux: type %s: "
+ "bounded by attribute %s",
+ (char *) key,
+ p->p_type_val_to_name[upper->value - 1]);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int policydb_bounds_sanity_check(struct policydb *p)
+{
+ int rc;
+
+ if (p->policyvers < POLICYDB_VERSION_BOUNDARY)
+ return 0;
+
+ rc = hashtab_map(p->p_users.table,
+ user_bounds_sanity_check, p);
+ if (rc)
+ return rc;
+
+ rc = hashtab_map(p->p_roles.table,
+ role_bounds_sanity_check, p);
+ if (rc)
+ return rc;
+
+ rc = hashtab_map(p->p_types.table,
+ type_bounds_sanity_check, p);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
extern int ss_initialized;
/*
@@ -1961,6 +2134,10 @@ int policydb_read(struct policydb *p, void *fp)
goto bad;
}
+ rc = policydb_bounds_sanity_check(p);
+ if (rc)
+ goto bad;
+
rc = 0;
out:
return rc;