|
@@ -61,6 +61,7 @@ enum smk_inos {
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
SMK_NET6ADDR = 23, /* single label IPv6 hosts */
|
|
|
#endif /* CONFIG_IPV6 */
|
|
|
+ SMK_RELABEL_SELF = 24, /* relabel possible without CAP_MAC_ADMIN */
|
|
|
};
|
|
|
|
|
|
/*
|
|
@@ -1914,10 +1915,10 @@ static void *onlycap_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
|
static int onlycap_seq_show(struct seq_file *s, void *v)
|
|
|
{
|
|
|
struct list_head *list = v;
|
|
|
- struct smack_onlycap *sop =
|
|
|
- list_entry_rcu(list, struct smack_onlycap, list);
|
|
|
+ struct smack_known_list_elem *sklep =
|
|
|
+ list_entry_rcu(list, struct smack_known_list_elem, list);
|
|
|
|
|
|
- seq_puts(s, sop->smk_label->smk_known);
|
|
|
+ seq_puts(s, sklep->smk_label->smk_known);
|
|
|
seq_putc(s, ' ');
|
|
|
|
|
|
return 0;
|
|
@@ -1973,6 +1974,54 @@ static void smk_list_swap_rcu(struct list_head *public,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * smk_parse_label_list - parse list of Smack labels, separated by spaces
|
|
|
+ *
|
|
|
+ * @data: the string to parse
|
|
|
+ * @private: destination list
|
|
|
+ *
|
|
|
+ * Returns zero on success or error code, as appropriate
|
|
|
+ */
|
|
|
+static int smk_parse_label_list(char *data, struct list_head *list)
|
|
|
+{
|
|
|
+ char *tok;
|
|
|
+ struct smack_known *skp;
|
|
|
+ struct smack_known_list_elem *sklep;
|
|
|
+
|
|
|
+ while ((tok = strsep(&data, " ")) != NULL) {
|
|
|
+ if (!*tok)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ skp = smk_import_entry(tok, 0);
|
|
|
+ if (IS_ERR(skp))
|
|
|
+ return PTR_ERR(skp);
|
|
|
+
|
|
|
+ sklep = kzalloc(sizeof(*sklep), GFP_KERNEL);
|
|
|
+ if (sklep == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ sklep->smk_label = skp;
|
|
|
+ list_add(&sklep->list, list);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * smk_destroy_label_list - destroy a list of smack_known_list_elem
|
|
|
+ * @head: header pointer of the list to destroy
|
|
|
+ */
|
|
|
+void smk_destroy_label_list(struct list_head *list)
|
|
|
+{
|
|
|
+ struct smack_known_list_elem *sklep;
|
|
|
+ struct smack_known_list_elem *sklep2;
|
|
|
+
|
|
|
+ list_for_each_entry_safe(sklep, sklep2, list, list)
|
|
|
+ kfree(sklep);
|
|
|
+
|
|
|
+ INIT_LIST_HEAD(list);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* smk_write_onlycap - write() for smackfs/onlycap
|
|
|
* @file: file pointer, not actually used
|
|
@@ -1986,13 +2035,8 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
|
|
|
size_t count, loff_t *ppos)
|
|
|
{
|
|
|
char *data;
|
|
|
- char *data_parse;
|
|
|
- char *tok;
|
|
|
- struct smack_known *skp;
|
|
|
- struct smack_onlycap *sop;
|
|
|
- struct smack_onlycap *sop2;
|
|
|
LIST_HEAD(list_tmp);
|
|
|
- int rc = count;
|
|
|
+ int rc;
|
|
|
|
|
|
if (!smack_privileged(CAP_MAC_ADMIN))
|
|
|
return -EPERM;
|
|
@@ -2006,26 +2050,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
|
|
|
return -EFAULT;
|
|
|
}
|
|
|
|
|
|
- data_parse = data;
|
|
|
- while ((tok = strsep(&data_parse, " ")) != NULL) {
|
|
|
- if (!*tok)
|
|
|
- continue;
|
|
|
-
|
|
|
- skp = smk_import_entry(tok, 0);
|
|
|
- if (IS_ERR(skp)) {
|
|
|
- rc = PTR_ERR(skp);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- sop = kzalloc(sizeof(*sop), GFP_KERNEL);
|
|
|
- if (sop == NULL) {
|
|
|
- rc = -ENOMEM;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- sop->smk_label = skp;
|
|
|
- list_add_rcu(&sop->list, &list_tmp);
|
|
|
- }
|
|
|
+ rc = smk_parse_label_list(data, &list_tmp);
|
|
|
kfree(data);
|
|
|
|
|
|
/*
|
|
@@ -2038,17 +2063,14 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
|
|
|
* But do so only on invalid label, not on system errors.
|
|
|
* The invalid label must be first to count as clearing attempt.
|
|
|
*/
|
|
|
- if (rc == -EINVAL && list_empty(&list_tmp))
|
|
|
- rc = count;
|
|
|
-
|
|
|
- if (rc >= 0) {
|
|
|
+ if (!rc || (rc == -EINVAL && list_empty(&list_tmp))) {
|
|
|
mutex_lock(&smack_onlycap_lock);
|
|
|
smk_list_swap_rcu(&smack_onlycap_list, &list_tmp);
|
|
|
mutex_unlock(&smack_onlycap_lock);
|
|
|
+ rc = count;
|
|
|
}
|
|
|
|
|
|
- list_for_each_entry_safe(sop, sop2, &list_tmp, list)
|
|
|
- kfree(sop);
|
|
|
+ smk_destroy_label_list(&list_tmp);
|
|
|
|
|
|
return rc;
|
|
|
}
|
|
@@ -2698,6 +2720,113 @@ static const struct file_operations smk_syslog_ops = {
|
|
|
.llseek = default_llseek,
|
|
|
};
|
|
|
|
|
|
+/*
|
|
|
+ * Seq_file read operations for /smack/relabel-self
|
|
|
+ */
|
|
|
+
|
|
|
+static void *relabel_self_seq_start(struct seq_file *s, loff_t *pos)
|
|
|
+{
|
|
|
+ struct task_smack *tsp = current_security();
|
|
|
+
|
|
|
+ return smk_seq_start(s, pos, &tsp->smk_relabel);
|
|
|
+}
|
|
|
+
|
|
|
+static void *relabel_self_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
|
+{
|
|
|
+ struct task_smack *tsp = current_security();
|
|
|
+
|
|
|
+ return smk_seq_next(s, v, pos, &tsp->smk_relabel);
|
|
|
+}
|
|
|
+
|
|
|
+static int relabel_self_seq_show(struct seq_file *s, void *v)
|
|
|
+{
|
|
|
+ struct list_head *list = v;
|
|
|
+ struct smack_known_list_elem *sklep =
|
|
|
+ list_entry(list, struct smack_known_list_elem, list);
|
|
|
+
|
|
|
+ seq_puts(s, sklep->smk_label->smk_known);
|
|
|
+ seq_putc(s, ' ');
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct seq_operations relabel_self_seq_ops = {
|
|
|
+ .start = relabel_self_seq_start,
|
|
|
+ .next = relabel_self_seq_next,
|
|
|
+ .show = relabel_self_seq_show,
|
|
|
+ .stop = smk_seq_stop,
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * smk_open_relabel_self - open() for /smack/relabel-self
|
|
|
+ * @inode: inode structure representing file
|
|
|
+ * @file: "relabel-self" file pointer
|
|
|
+ *
|
|
|
+ * Connect our relabel_self_seq_* operations with /smack/relabel-self
|
|
|
+ * file_operations
|
|
|
+ */
|
|
|
+static int smk_open_relabel_self(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ return seq_open(file, &relabel_self_seq_ops);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * smk_write_relabel_self - write() for /smack/relabel-self
|
|
|
+ * @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_self(struct file *file, const char __user *buf,
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct task_smack *tsp = current_security();
|
|
|
+ char *data;
|
|
|
+ int rc;
|
|
|
+ LIST_HEAD(list_tmp);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Must have privilege.
|
|
|
+ */
|
|
|
+ if (!smack_privileged(CAP_MAC_ADMIN))
|
|
|
+ return -EPERM;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Enough data must be present.
|
|
|
+ */
|
|
|
+ if (*ppos != 0)
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ rc = smk_parse_label_list(data, &list_tmp);
|
|
|
+ kfree(data);
|
|
|
+
|
|
|
+ if (!rc || (rc == -EINVAL && list_empty(&list_tmp))) {
|
|
|
+ smk_destroy_label_list(&tsp->smk_relabel);
|
|
|
+ list_splice(&list_tmp, &tsp->smk_relabel);
|
|
|
+ return count;
|
|
|
+ }
|
|
|
+
|
|
|
+ smk_destroy_label_list(&list_tmp);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct file_operations smk_relabel_self_ops = {
|
|
|
+ .open = smk_open_relabel_self,
|
|
|
+ .read = seq_read,
|
|
|
+ .llseek = seq_lseek,
|
|
|
+ .write = smk_write_relabel_self,
|
|
|
+ .release = seq_release,
|
|
|
+};
|
|
|
|
|
|
/**
|
|
|
* smk_read_ptrace - read() for /smack/ptrace
|
|
@@ -2824,6 +2953,9 @@ 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_SELF] = {
|
|
|
+ "relabel-self", &smk_relabel_self_ops,
|
|
|
+ S_IRUGO|S_IWUGO},
|
|
|
/* last one */
|
|
|
{""}
|
|
|
};
|