diff options
Diffstat (limited to 'security/smack/smackfs.c')
-rw-r--r-- | security/smack/smackfs.c | 277 |
1 files changed, 204 insertions, 73 deletions
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index f93460156dc..6aceef518a4 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -44,6 +44,7 @@ enum smk_inos { SMK_ONLYCAP = 9, /* the only "capable" label */ SMK_LOGGING = 10, /* logging */ SMK_LOAD_SELF = 11, /* task specific rules */ + SMK_ACCESSES = 12, /* access policy */ }; /* @@ -85,6 +86,16 @@ char *smack_onlycap; */ LIST_HEAD(smk_netlbladdr_list); + +/* + * Rule lists are maintained for each label. + * This master list is just for reading /smack/load. + */ +struct smack_master_list { + struct list_head list; + struct smack_rule *smk_rule; +}; + LIST_HEAD(smack_rule_list); static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; @@ -92,7 +103,7 @@ static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; const char *smack_cipso_option = SMACK_CIPSO_OPTION; -#define SEQ_READ_FINISHED 1 +#define SEQ_READ_FINISHED ((loff_t)-1) /* * Values for parsing cipso rules @@ -159,9 +170,13 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, mutex_lock(rule_lock); + /* + * Because the object label is less likely to match + * than the subject label check it first + */ list_for_each_entry_rcu(sp, rule_list, list) { - if (sp->smk_subject == srp->smk_subject && - sp->smk_object == srp->smk_object) { + if (sp->smk_object == srp->smk_object && + sp->smk_subject == srp->smk_subject) { found = 1; sp->smk_access = srp->smk_access; break; @@ -176,6 +191,99 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, } /** + * smk_parse_rule - parse Smack rule from load string + * @data: string to be parsed whose size is SMK_LOADLEN + * @rule: Smack rule + * @import: if non-zero, import labels + */ +static int smk_parse_rule(const char *data, struct smack_rule *rule, int import) +{ + char smack[SMK_LABELLEN]; + struct smack_known *skp; + + if (import) { + rule->smk_subject = smk_import(data, 0); + if (rule->smk_subject == NULL) + return -1; + + rule->smk_object = smk_import(data + SMK_LABELLEN, 0); + if (rule->smk_object == NULL) + return -1; + } else { + smk_parse_smack(data, 0, smack); + skp = smk_find_entry(smack); + if (skp == NULL) + return -1; + rule->smk_subject = skp->smk_known; + + smk_parse_smack(data + SMK_LABELLEN, 0, smack); + skp = smk_find_entry(smack); + if (skp == NULL) + return -1; + rule->smk_object = skp->smk_known; + } + + rule->smk_access = 0; + + switch (data[SMK_LABELLEN + SMK_LABELLEN]) { + case '-': + break; + case 'r': + case 'R': + rule->smk_access |= MAY_READ; + break; + default: + return -1; + } + + switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) { + case '-': + break; + case 'w': + case 'W': + rule->smk_access |= MAY_WRITE; + break; + default: + return -1; + } + + switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) { + case '-': + break; + case 'x': + case 'X': + rule->smk_access |= MAY_EXEC; + break; + default: + return -1; + } + + switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) { + case '-': + break; + case 'a': + case 'A': + rule->smk_access |= MAY_APPEND; + break; + default: + return -1; + } + + switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) { + case '-': + break; + case 't': + case 'T': + rule->smk_access |= MAY_TRANSMUTE; + break; + default: + return -1; + } + + return 0; +} + +/** * smk_write_load_list - write() for any /smack/load * @file: file pointer, not actually used * @buf: where to get the data from @@ -197,9 +305,12 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf, struct list_head *rule_list, struct mutex *rule_lock) { + struct smack_master_list *smlp; + struct smack_known *skp; struct smack_rule *rule; char *data; int rc = -EINVAL; + int load = 0; /* * No partial writes. @@ -234,69 +345,14 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf, goto out; } - rule->smk_subject = smk_import(data, 0); - if (rule->smk_subject == NULL) - goto out_free_rule; - - rule->smk_object = smk_import(data + SMK_LABELLEN, 0); - if (rule->smk_object == NULL) + if (smk_parse_rule(data, rule, 1)) goto out_free_rule; - rule->smk_access = 0; - - switch (data[SMK_LABELLEN + SMK_LABELLEN]) { - case '-': - break; - case 'r': - case 'R': - rule->smk_access |= MAY_READ; - break; - default: - goto out_free_rule; - } - - switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) { - case '-': - break; - case 'w': - case 'W': - rule->smk_access |= MAY_WRITE; - break; - default: - goto out_free_rule; - } - - switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) { - case '-': - break; - case 'x': - case 'X': - rule->smk_access |= MAY_EXEC; - break; - default: - goto out_free_rule; - } - - switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) { - case '-': - break; - case 'a': - case 'A': - rule->smk_access |= MAY_APPEND; - break; - default: - goto out_free_rule; - } - - switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) { - case '-': - break; - case 't': - case 'T': - rule->smk_access |= MAY_TRANSMUTE; - break; - default: - goto out_free_rule; + if (rule_list == NULL) { + load = 1; + skp = smk_find_entry(rule->smk_subject); + rule_list = &skp->smk_rules; + rule_lock = &skp->smk_rules_lock; } rc = count; @@ -304,8 +360,15 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf, * smk_set_access returns true if there was already a rule * for the subject/object pair, and false if it was new. */ - if (!smk_set_access(rule, rule_list, rule_lock)) + if (!smk_set_access(rule, rule_list, rule_lock)) { + smlp = kzalloc(sizeof(*smlp), GFP_KERNEL); + if (smlp != NULL) { + smlp->smk_rule = rule; + list_add_rcu(&smlp->list, &smack_rule_list); + } else + rc = -ENOMEM; goto out; + } out_free_rule: kfree(rule); @@ -321,11 +384,24 @@ out: static void *load_seq_start(struct seq_file *s, loff_t *pos) { - if (*pos == SEQ_READ_FINISHED) + struct list_head *list; + + /* + * This is 0 the first time through. + */ + if (s->index == 0) + s->private = &smack_rule_list; + + if (s->private == NULL) return NULL; - if (list_empty(&smack_rule_list)) + + list = s->private; + if (list_empty(list)) return NULL; - return smack_rule_list.next; + + if (s->index == 0) + return list->next; + return list; } static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos) @@ -333,17 +409,19 @@ static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos) struct list_head *list = v; if (list_is_last(list, &smack_rule_list)) { - *pos = SEQ_READ_FINISHED; + s->private = NULL; return NULL; } + s->private = list->next; return list->next; } static int load_seq_show(struct seq_file *s, void *v) { struct list_head *list = v; - struct smack_rule *srp = - list_entry(list, struct smack_rule, list); + struct smack_master_list *smlp = + list_entry(list, struct smack_master_list, list); + struct smack_rule *srp = smlp->smk_rule; seq_printf(s, "%s %s", (char *)srp->smk_subject, (char *)srp->smk_object); @@ -412,8 +490,7 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, if (!capable(CAP_MAC_ADMIN)) return -EPERM; - return smk_write_load_list(file, buf, count, ppos, &smack_rule_list, - &smack_list_lock); + return smk_write_load_list(file, buf, count, ppos, NULL, NULL); } static const struct file_operations smk_load_ops = { @@ -1425,6 +1502,44 @@ static const struct file_operations smk_load_self_ops = { .write = smk_write_load_self, .release = seq_release, }; + +/** + * smk_write_access - handle access check transaction + * @file: file pointer + * @buf: data from user space + * @count: bytes sent + * @ppos: where to start - must be 0 + */ +static ssize_t smk_write_access(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct smack_rule rule; + char *data; + int res; + + data = simple_transaction_get(file, buf, count); + if (IS_ERR(data)) + return PTR_ERR(data); + + if (count < SMK_LOADLEN || smk_parse_rule(data, &rule, 0)) + return -EINVAL; + + res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access, + NULL); + data[0] = res == 0 ? '1' : '0'; + data[1] = '\0'; + + simple_transaction_set(file, 2); + return SMK_LOADLEN; +} + +static const struct file_operations smk_access_ops = { + .write = smk_write_access, + .read = simple_transaction_read, + .release = simple_transaction_release, + .llseek = generic_file_llseek, +}; + /** * smk_fill_super - fill the /smackfs superblock * @sb: the empty superblock @@ -1459,6 +1574,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) "logging", &smk_logging_ops, S_IRUGO|S_IWUSR}, [SMK_LOAD_SELF] = { "load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO}, + [SMK_ACCESSES] = { + "access", &smk_access_ops, S_IRUGO|S_IWUGO}, /* last one */ {""} }; @@ -1534,6 +1651,20 @@ static int __init init_smk_fs(void) smk_cipso_doi(); smk_unlbl_ambient(NULL); + mutex_init(&smack_known_floor.smk_rules_lock); + mutex_init(&smack_known_hat.smk_rules_lock); + mutex_init(&smack_known_huh.smk_rules_lock); + mutex_init(&smack_known_invalid.smk_rules_lock); + mutex_init(&smack_known_star.smk_rules_lock); + mutex_init(&smack_known_web.smk_rules_lock); + + INIT_LIST_HEAD(&smack_known_floor.smk_rules); + INIT_LIST_HEAD(&smack_known_hat.smk_rules); + INIT_LIST_HEAD(&smack_known_huh.smk_rules); + INIT_LIST_HEAD(&smack_known_invalid.smk_rules); + INIT_LIST_HEAD(&smack_known_star.smk_rules); + INIT_LIST_HEAD(&smack_known_web.smk_rules); + return err; } |