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 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 | 8 +++ security/smack/smack_lsm.c | 17 +++++ security/smack/smackfs.c | 147 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 185 insertions(+) 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 d17580e..db4a1a3 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -193,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 */ @@ -333,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 0766a4b..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 */ @@ -3554,6 +3556,8 @@ static int smack_setprocattr(struct task_struct *p, char *name, 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 @@ -3581,6 +3585,19 @@ 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; diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 18c1b934..11012f1 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -62,6 +62,7 @@ enum smk_inos { SMK_NET6ADDR = 23, /* single label IPv6 hosts */ #endif /* CONFIG_IPV6 */ SMK_RELABEL_POSSIBLE = 24, /* relabel possible without CAP_MAC_ADMIN */ + SMK_RELABEL_LIST = 25, }; /* @@ -73,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. @@ -2770,6 +2772,148 @@ static const struct file_operations smk_relabel_possible_ops = { .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 @@ -2898,6 +3042,9 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) [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