diff options
Diffstat (limited to 'security/smack/smackfs.c')
-rw-r--r-- | security/smack/smackfs.c | 202 |
1 files changed, 167 insertions, 35 deletions
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index ce8d503a18a0..94bd9e41c9ec 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -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; @@ -1974,6 +1975,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 * @buf: where to get the data from @@ -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 */ {""} }; |