[PATCH v3 1/1] Introducing domain transition mechanism into Smack.

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

 



This feature consists of two, new kernel interfaces:

- <smack_fs>/relabel-possible - for setting transition privilege

This flag can be set only by process to itself and only with CAP_MAC_ADMIN
capability. With this flag on, process can change it's label without
CAP_MAC_ADMIN but only once. After label changing flag is unset.

- <smack_fs>/relabel-list - for transition-labels list

This list is used to control smack label transition mechanism.
Process can transit to new label only if label is on the list.
Only process with CAP_MAC_ADMIN capability can add label to this list.

Changes in v2:
* use list_for_each_entry instead of _rcu during label write
* added missing description in security/Smack.txt

Changes in v3:
* squashed into one commit

Signed-off-by: Zbigniew Jasinski <z.jasinski@xxxxxxxxxxx>
Signed-off-by: Rafal Krypa <r.krypa@xxxxxxxxxxx>
---
 Documentation/security/Smack.txt |  13 +++
 security/smack/smack.h           |   9 ++
 security/smack/smack_lsm.c       |  27 ++++-
 security/smack/smackfs.c         | 221 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 268 insertions(+), 2 deletions(-)

diff --git a/Documentation/security/Smack.txt b/Documentation/security/Smack.txt
index 5e6d07f..0ffd194 100644
--- a/Documentation/security/Smack.txt
+++ b/Documentation/security/Smack.txt
@@ -255,6 +255,19 @@ unconfined
 	the access permitted if it wouldn't be otherwise. Note that this
 	is dangerous and can ruin the proper labeling of your system.
 	It should never be used in production.
+relabel-possible
+	This interface is used to set relabel-possible flag. A process
+	with this flag is able to change its label without CAP_MAC_ADMIN,
+	but only once. After label transition this flag is zeroed.
+	0 - default: process is not allowed to label transition
+	1 - process is allowed to one-time label tranistion
+relabel-list
+	This interface contains a list of labels, in which process can
+	transition to. The format accepted on write is:
+                "%s"
+	for adding label, and:
+		"-%s"
+	for removing label from list.
 
 If you are using the smackload utility
 you can add access rules in /etc/smack/accesses. They take the form:
diff --git a/security/smack/smack.h b/security/smack/smack.h
index fff0c61..db4a1a3 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -115,6 +115,7 @@ struct task_smack {
 	struct smack_known	*smk_forked;	/* label when forked */
 	struct list_head	smk_rules;	/* per task access rules */
 	struct mutex		smk_rules_lock;	/* lock for the rules */
+	int	smk_relabel;	/* task can change its label */
 };
 
 #define	SMK_INODE_INSTANT	0x01	/* inode is instantiated */
@@ -192,6 +193,12 @@ enum {
 	Opt_fstransmute = 5,
 };
 
+struct smack_relabel {
+	struct rcu_head         rcu;
+	struct list_head	list;
+	struct smack_known	*smk_label;
+};
+
 /*
  * Mount options
  */
@@ -332,6 +339,8 @@ extern struct list_head smk_net6addr_list;
 extern struct mutex     smack_onlycap_lock;
 extern struct list_head smack_onlycap_list;
 
+extern struct list_head smack_relabel_list;
+
 #define SMACK_HASH_SLOTS 16
 extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
 
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 996c889..596f270 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -66,6 +66,8 @@ static const match_table_t smk_mount_tokens = {
 	{Opt_error, NULL},
 };
 
+LIST_HEAD(smack_relabel_list);
+
 #ifdef CONFIG_SECURITY_SMACK_BRINGUP
 static char *smk_bu_mess[] = {
 	"Bringup Error",	/* Unused */
@@ -325,6 +327,7 @@ static struct task_smack *new_task_smack(struct smack_known *task,
 
 	tsp->smk_task = task;
 	tsp->smk_forked = forked;
+	tsp->smk_relabel = 0;
 	INIT_LIST_HEAD(&tsp->smk_rules);
 	mutex_init(&tsp->smk_rules_lock);
 
@@ -1953,6 +1956,7 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
 	if (rc != 0)
 		return rc;
 
+	new_tsp->smk_relabel = old_tsp->smk_relabel;
 	new->security = new_tsp;
 	return 0;
 }
@@ -3549,9 +3553,11 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
 static int smack_setprocattr(struct task_struct *p, char *name,
 			     void *value, size_t size)
 {
-	struct task_smack *tsp;
+	struct task_smack *tsp = current_security();
 	struct cred *new;
 	struct smack_known *skp;
+	struct smack_relabel *srp;
+	int rc;
 
 	/*
 	 * Changing another process' Smack value is too dangerous
@@ -3560,7 +3566,7 @@ static int smack_setprocattr(struct task_struct *p, char *name,
 	if (p != current)
 		return -EPERM;
 
-	if (!smack_privileged(CAP_MAC_ADMIN))
+	if (!smack_privileged(CAP_MAC_ADMIN) && !tsp->smk_relabel)
 		return -EPERM;
 
 	if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
@@ -3579,12 +3585,29 @@ static int smack_setprocattr(struct task_struct *p, char *name,
 	if (skp == &smack_known_web)
 		return -EPERM;
 
+	if (tsp->smk_relabel) {
+		rc = -EPERM;
+		rcu_read_lock();
+		list_for_each_entry_rcu(srp, &smack_relabel_list, list)
+			if (srp->smk_label == skp) {
+				rc = 0;
+				break;
+			}
+		rcu_read_unlock();
+		if (rc)
+			return rc;
+	}
+
 	new = prepare_creds();
 	if (new == NULL)
 		return -ENOMEM;
 
 	tsp = new->security;
 	tsp->smk_task = skp;
+	/*
+	 * process can change its label only once
+	 */
+	tsp->smk_relabel = 0;
 
 	commit_creds(new);
 	return size;
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index c20b154..11012f1 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -61,6 +61,8 @@ enum smk_inos {
 #if IS_ENABLED(CONFIG_IPV6)
 	SMK_NET6ADDR	= 23,	/* single label IPv6 hosts */
 #endif /* CONFIG_IPV6 */
+	SMK_RELABEL_POSSIBLE = 24, /* relabel possible without CAP_MAC_ADMIN */
+	SMK_RELABEL_LIST = 25,
 };
 
 /*
@@ -72,6 +74,7 @@ static DEFINE_MUTEX(smk_net4addr_lock);
 #if IS_ENABLED(CONFIG_IPV6)
 static DEFINE_MUTEX(smk_net6addr_lock);
 #endif /* CONFIG_IPV6 */
+static DEFINE_MUTEX(smack_relabel_list_lock);
 
 /*
  * This is the "ambient" label for network traffic.
@@ -2700,6 +2703,218 @@ static const struct file_operations smk_syslog_ops = {
 
 
 /**
+ * smk_read_relabel - read() for smackfs/relabel
+ * @filp: file pointer, not actually used
+ * @buf: where to put the result
+ * @count: maximum to send along
+ * @ppos: where to start
+ *
+ * Returns number of bytes read or error code, as appropriate
+ */
+static ssize_t smk_read_relabel(struct file *filp, char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct task_smack *tsp = current_security();
+	char temp[32];
+	ssize_t rc;
+
+	if (*ppos != 0)
+		return 0;
+
+	sprintf(temp, "%d\n", tsp->smk_relabel);
+	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+	return rc;
+}
+
+/**
+ * smk_write_relabel - write() for /smack/relabel
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_relabel(struct file *file, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct task_smack *tsp = current_security();
+	struct cred *creds;
+	int r;
+
+	if (!smack_privileged(CAP_MAC_ADMIN))
+		return -EPERM;
+
+	if (count == 0 || count > 2)
+		return -EINVAL;
+
+	if (kstrtoint_from_user(buf, count, 0, &r))
+		return -EFAULT;
+
+	if (r != 0 && r != 1)
+		return -EINVAL;
+
+	creds = prepare_creds();
+	if (creds == NULL)
+		return -ENOMEM;
+
+	tsp = creds->security;
+	tsp->smk_relabel = r;
+
+	commit_creds(creds);
+	return count;
+}
+
+
+static const struct file_operations smk_relabel_possible_ops = {
+	.read		= smk_read_relabel,
+	.write		= smk_write_relabel,
+	.llseek		= default_llseek,
+};
+
+/*
+ * Seq_file read operations for /smack/relabel-list
+ */
+
+static void *relabel_list_seq_start(struct seq_file *s, loff_t *pos)
+{
+	return smk_seq_start(s, pos, &smack_relabel_list);
+}
+
+static void *relabel_list_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+	return smk_seq_next(s, v, pos, &smack_relabel_list);
+}
+
+static int relabel_list_seq_show(struct seq_file *s, void *v)
+{
+	struct list_head *list = v;
+	struct smack_relabel *srp =
+		list_entry_rcu(list, struct smack_relabel, list);
+
+	seq_printf(s, "%s\n", srp->smk_label->smk_known);
+
+	return 0;
+}
+
+static const struct seq_operations relabel_list_seq_ops = {
+	.start = relabel_list_seq_start,
+	.next  = relabel_list_seq_next,
+	.show  = relabel_list_seq_show,
+	.stop  = smk_seq_stop,
+};
+
+/**
+ * smk_open_relabel_list - open() for /smack/relabel-list
+ * @inode: inode structure representing file
+ * @file: "relabel-list" file pointer
+ *
+ * For reading, use load2_seq_* seq_file reading operations.
+ */
+static int smk_open_relabel_list(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &relabel_list_seq_ops);
+}
+
+/**
+ * smk_write_relabel_list - write() for /smack/relabel-list
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ *
+ */
+static ssize_t smk_write_relabel_list(struct file *file, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct smack_known *skp;
+	struct smack_relabel *srp;
+	int rc = count;
+	int remove;
+	char *data;
+	char *label;
+
+	/*
+	 * Must have privilege.
+	 */
+	if (!smack_privileged(CAP_MAC_ADMIN))
+		return -EPERM;
+
+	/*
+	 * Enough data must be present.
+	 * One label per line.
+	 */
+	if (*ppos != 0 || count >= SMK_LONGLABEL)
+		return -EINVAL;
+
+	data = kzalloc(count + 1, GFP_KERNEL);
+	if (data == NULL)
+		return -ENOMEM;
+
+	if (copy_from_user(data, buf, count) != 0) {
+		kfree(data);
+		return -EFAULT;
+	}
+
+	if (data[0] == '-') {
+		remove = 1;
+		label = smk_parse_smack(data + 1, count - 1);
+		if (IS_ERR(label)) {
+			kfree(data);
+			return PTR_ERR(label);
+		}
+		skp = smk_find_entry(label);
+		kfree(label);
+	} else {
+		remove = 0;
+		skp = smk_import_entry(data, count);
+	}
+	kfree(data);
+
+	if (IS_ERR(skp))
+		return PTR_ERR(skp);
+
+	mutex_lock(&smack_relabel_list_lock);
+	list_for_each_entry(srp, &smack_relabel_list, list)
+		if (srp->smk_label == skp) {
+			if (remove) {
+				list_del_rcu(&srp->list);
+				mutex_unlock(&smack_relabel_list_lock);
+				kfree_rcu(srp, rcu);
+				return rc;
+			} else
+				goto out;
+		}
+
+	/* Entry not found on smack_relabel_list */
+	if (remove) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	srp = kzalloc(sizeof(*srp), GFP_KERNEL);
+	if (srp == NULL) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	srp->smk_label = skp;
+	list_add_rcu(&srp->list, &smack_relabel_list);
+
+out:
+	mutex_unlock(&smack_relabel_list_lock);
+	return rc;
+}
+
+static const struct file_operations smk_relabel_list_ops = {
+	.open           = smk_open_relabel_list,
+	.read		= seq_read,
+	.llseek         = seq_lseek,
+	.write		= smk_write_relabel_list,
+	.release        = seq_release,
+};
+
+/**
  * smk_read_ptrace - read() for /smack/ptrace
  * @filp: file pointer, not actually used
  * @buf: where to put the result
@@ -2824,6 +3039,12 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
 		[SMK_NET6ADDR] = {
 			"ipv6host", &smk_net6addr_ops, S_IRUGO|S_IWUSR},
 #endif /* CONFIG_IPV6 */
+		[SMK_RELABEL_POSSIBLE] = {
+			"relabel-possible", &smk_relabel_possible_ops,
+				S_IRUGO|S_IWUGO},
+		[SMK_RELABEL_LIST] = {
+			"relabel-list", &smk_relabel_list_ops,
+				S_IRUGO|S_IWUGO},
 		/* last one */
 			{""}
 	};
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-doc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux