[PATCH 1/3] Thread/Child-Domain Assignment

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



[1/3] thread-context-kernel.1.patch
  This patch enables to assign a thread a "weaker" hierarchical domain,
  only if the destinated domain is a child of the current domain.
  Hierachy relationships are defined in the policy version 24.
  This patch also enables to read the new version of policy.


Signed-off-by: KaiGai Kohei <kaigai@xxxxxxxxxxxxx>
----
 security/selinux/hooks.c            |   11 +++++-
 security/selinux/include/security.h |    5 ++-
 security/selinux/ss/policydb.c      |   75 ++++++++++++++++++++++++++++++++--
 security/selinux/ss/policydb.h      |    2 +
 security/selinux/ss/services.c      |   60 ++++++++++++++++++++++++++++
 5 files changed, 146 insertions(+), 7 deletions(-)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index bc1c3ae..d4c1c5c 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -5181,7 +5181,12 @@ static int selinux_setprocattr(struct task_struct *p,
 		if (sid == 0)
 			return -EINVAL;

-		/* Only allow single threaded processes to change context */
+		/*
+		 * SELinux allows to change context in the following case only.
+		 *  - Single threaded processes.
+		 *  - Multi threaded processes intend to change its context into
+		 *    lower or same domain in hierarchy relationship.
+		 */
 		if (atomic_read(&p->mm->mm_users) != 1) {
 			struct task_struct *g, *t;
 			struct mm_struct *mm = p->mm;
@@ -5189,11 +5194,15 @@ static int selinux_setprocattr(struct task_struct *p,
 			do_each_thread(g, t) {
 				if (t->mm == mm && t != p) {
 					read_unlock(&tasklist_lock);
+
+					if (!security_check_hierarchy(tsec->sid, sid))
+						goto hierarchy_ok;
 					return -EPERM;
 				}
 			} while_each_thread(g, t);
 			read_unlock(&tasklist_lock);
 		}
+hierarchy_ok:

 		/* Check permissions for the transition. */
 		error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index ad30ac4..97fa9bb 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -27,13 +27,14 @@
 #define POLICYDB_VERSION_RANGETRANS	21
 #define POLICYDB_VERSION_POLCAP		22
 #define POLICYDB_VERSION_PERMISSIVE	23
+#define POLICYDB_VERSION_HIERARCHY	24

 /* Range of policy versions we understand*/
 #define POLICYDB_VERSION_MIN   POLICYDB_VERSION_BASE
 #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
 #define POLICYDB_VERSION_MAX	CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
 #else
-#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_PERMISSIVE
+#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_HIERARCHY
 #endif

 #define CONTEXT_MNT	0x01
@@ -118,6 +119,8 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
 				 u32 xfrm_sid,
 				 u32 *peer_sid);

+int security_check_hierarchy(u32 old_sid, u32 new_sid);
+
 int security_get_classes(char ***classes, int *nclasses);
 int security_get_permissions(char *class, char ***perms, int *nperms);
 int security_get_reject_unknown(void);
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 84f8cc7..e388f7a 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -116,7 +116,12 @@ static struct policydb_compat_info policydb_compat[] = {
 		.version	= POLICYDB_VERSION_PERMISSIVE,
 		.sym_num	= SYM_NUM,
 		.ocon_num	= OCON_NUM,
-	}
+	},
+	{
+		.version	= POLICYDB_VERSION_HIERARCHY,
+		.sym_num	= SYM_NUM,
+		.ocon_num	= OCON_NUM,
+	},
 };

 static struct policydb_compat_info *policydb_lookup_compat(int version)
@@ -270,9 +275,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->parent > 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;
@@ -397,6 +405,46 @@ static void symtab_hash_eval(struct symtab *s)
 }
 #endif

+static int type_hierarchy_sanity_checks(struct policydb *p)
+{
+	struct type_datum *type;
+	struct ebitmap e;
+	int rc = 0, i;
+
+	if (p->policyvers < POLICYDB_VERSION_HIERARCHY)
+		return 0;
+
+	for (i = 0; i < p->p_types.nprim; i++) {
+		type = p->type_val_to_struct[i];
+
+		if (!type || !type->parent)
+			continue;
+
+		ebitmap_init(&e);
+		while (type) {
+			if (ebitmap_get_bit(&e, type->value - 1)) {
+				printk(KERN_ERR "Hierarchy type looped at %s\n",
+				       p->p_type_val_to_name[type->value - 1]);
+				rc = -EINVAL;
+				break;
+			}
+
+			rc = ebitmap_set_bit(&e, type->value - 1, 1);
+			if (rc)
+				break;
+
+			if (!type->parent)
+				break;
+
+			type = p->type_val_to_struct[type->parent - 1];
+		}
+		ebitmap_destroy(&e);
+		if (rc)
+			return rc;
+	}
+	return 0;
+}
+
 /*
  * Define the other val_to_name and val_to_struct arrays
  * in a policy database structure.
@@ -438,6 +486,14 @@ static int policydb_index_others(struct policydb *p)
 		goto out;
 	}

+	p->type_val_to_struct =
+		kzalloc(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;
@@ -455,6 +511,7 @@ static int policydb_index_others(struct policydb *p)
 			goto out;
 	}

+	rc = type_hierarchy_sanity_checks(p);
 out:
 	return rc;
 }
@@ -625,6 +682,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);

@@ -1236,8 +1294,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 to_read, rc;
+	__le32 buf[4];
 	u32 len;

 	typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL);
@@ -1246,13 +1304,20 @@ 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_HIERARCHY)
+		to_read = sizeof(u32) * 3;
+	else
+		to_read = sizeof(u32) * 4;
+
+	rc = next_entry(buf, fp, 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_HIERARCHY)
+		typdatum->parent = le32_to_cpu(buf[3]);

 	key = kmalloc(len + 1, GFP_KERNEL);
 	if (!key) {
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 4253370..f3484d4 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -81,6 +81,7 @@ struct role_allow {
 /* Type attributes */
 struct type_datum {
 	u32 value;		/* internal type value */
+	u32 parent;		/* parent type, if hierarchy related */
 	unsigned char primary;	/* primary name? */
 };

@@ -209,6 +210,7 @@ struct policydb {
 	struct class_datum **class_val_to_struct;
 	struct role_datum **role_val_to_struct;
 	struct user_datum **user_val_to_struct;
+	struct type_datum **type_val_to_struct;

 	/* type enforcement access vectors and transitions */
 	struct avtab te_avtab;
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index dcc2e1c..3ecd793 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2160,6 +2160,66 @@ out_slowpath:
 	return rc;
 }

+/*
+ * security_check_hierarchy
+ *
+ * It returns 0, if @old_sid is same or upper type of @new_sid in hierarchy
+ * relationship directly/indirectly. 1 means @old_sid is not a child type
+ * of @new_sid, and negative returns are error.
+ */
+int security_check_hierarchy(u32 old_sid, u32 new_sid)
+{
+	struct context *old_context, *new_context;
+	struct type_datum *type;
+	int index;
+	int rc = -EINVAL;
+
+	read_lock(&policy_rwlock);
+
+	old_context = sidtab_search(&sidtab, old_sid);
+	if (!old_context) {
+		printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n",
+		       __func__, old_sid);
+		goto out;
+	}
+
+	new_context = sidtab_search(&sidtab, new_sid);
+	if (!new_context) {
+		printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n",
+		       __func__, new_sid);
+		goto out;
+	}
+
+	/* domain unchanged */
+	if (old_context->type == new_context->type) {
+		rc = 0;
+		goto out;
+	}
+
+	/* check domain hierarchy */
+	index = new_context->type;
+	while (true) {
+		type = policydb.type_val_to_struct[index - 1];
+		if (!type)
+			break;
+
+		/* No hierarchy relationship */
+		if (type->parent == 0) {
+			rc = 1;
+			break;
+		}
+		if (type->parent == old_context->type) {
+			rc = 0;
+			break;
+		}
+		index = type->parent;
+	}
+out:
+	read_unlock(&policy_rwlock);
+
+	return rc;
+}
+
 static int get_classes_callback(void *k, void *d, void *args)
 {
 	struct class_datum *datum = d;


-- 
OSS Platform Development Division, NEC
KaiGai Kohei <kaigai@xxxxxxxxxxxxx>

--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with
the words "unsubscribe selinux" without quotes as the message.

[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux