diff options
Diffstat (limited to 'security/smack')
-rw-r--r-- | security/smack/smack.h | 28 | ||||
-rw-r--r-- | security/smack/smack_access.c | 63 | ||||
-rw-r--r-- | security/smack/smack_lsm.c | 19 | ||||
-rw-r--r-- | security/smack/smackfs.c | 180 |
4 files changed, 168 insertions, 122 deletions
diff --git a/security/smack/smack.h b/security/smack/smack.h index b79582e4fbfd..64164f8fde70 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -18,6 +18,8 @@ #include <linux/security.h> #include <linux/in.h> #include <net/netlabel.h> +#include <linux/list.h> +#include <linux/rculist.h> /* * Why 23? CIPSO is constrained to 30, so a 32 byte buffer is @@ -59,17 +61,10 @@ struct inode_smack { * A label access rule. */ struct smack_rule { - char *smk_subject; - char *smk_object; - int smk_access; -}; - -/* - * An entry in the table of permitted label accesses. - */ -struct smk_list_entry { - struct smk_list_entry *smk_next; - struct smack_rule smk_rule; + struct list_head list; + char *smk_subject; + char *smk_object; + int smk_access; }; /* @@ -85,7 +80,7 @@ struct smack_cipso { * An entry in the table identifying hosts. */ struct smk_netlbladdr { - struct smk_netlbladdr *smk_next; + struct list_head list; struct sockaddr_in smk_host; /* network address */ struct in_addr smk_mask; /* network mask */ char *smk_label; /* label */ @@ -113,7 +108,7 @@ struct smk_netlbladdr { * the cipso direct mapping in used internally. */ struct smack_known { - struct smack_known *smk_next; + struct list_head list; char smk_known[SMK_LABELLEN]; u32 smk_secid; struct smack_cipso *smk_cipso; @@ -206,7 +201,6 @@ extern int smack_cipso_direct; extern char *smack_net_ambient; extern char *smack_onlycap; -extern struct smack_known *smack_known; extern struct smack_known smack_known_floor; extern struct smack_known smack_known_hat; extern struct smack_known smack_known_huh; @@ -214,8 +208,10 @@ extern struct smack_known smack_known_invalid; extern struct smack_known smack_known_star; extern struct smack_known smack_known_web; -extern struct smk_list_entry *smack_list; -extern struct smk_netlbladdr *smack_netlbladdrs; +extern struct list_head smack_known_list; +extern struct list_head smack_rule_list; +extern struct list_head smk_netlbladdr_list; + extern struct security_operations smack_ops; /* diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index cfa19ca125e3..58564195bb09 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -16,48 +16,42 @@ #include "smack.h" struct smack_known smack_known_huh = { - .smk_next = NULL, .smk_known = "?", .smk_secid = 2, .smk_cipso = NULL, }; struct smack_known smack_known_hat = { - .smk_next = &smack_known_huh, .smk_known = "^", .smk_secid = 3, .smk_cipso = NULL, }; struct smack_known smack_known_star = { - .smk_next = &smack_known_hat, .smk_known = "*", .smk_secid = 4, .smk_cipso = NULL, }; struct smack_known smack_known_floor = { - .smk_next = &smack_known_star, .smk_known = "_", .smk_secid = 5, .smk_cipso = NULL, }; struct smack_known smack_known_invalid = { - .smk_next = &smack_known_floor, .smk_known = "", .smk_secid = 6, .smk_cipso = NULL, }; struct smack_known smack_known_web = { - .smk_next = &smack_known_invalid, .smk_known = "@", .smk_secid = 7, .smk_cipso = NULL, }; -struct smack_known *smack_known = &smack_known_web; +LIST_HEAD(smack_known_list); /* * The initial value needs to be bigger than any of the @@ -87,7 +81,6 @@ static u32 smack_next_secid = 10; int smk_access(char *subject_label, char *object_label, int request) { u32 may = MAY_NOT; - struct smk_list_entry *sp; struct smack_rule *srp; /* @@ -139,9 +132,8 @@ int smk_access(char *subject_label, char *object_label, int request) * access (e.g. read is included in readwrite) it's * good. */ - for (sp = smack_list; sp != NULL; sp = sp->smk_next) { - srp = &sp->smk_rule; - + rcu_read_lock(); + list_for_each_entry_rcu(srp, &smack_rule_list, list) { if (srp->smk_subject == subject_label || strcmp(srp->smk_subject, subject_label) == 0) { if (srp->smk_object == object_label || @@ -151,6 +143,7 @@ int smk_access(char *subject_label, char *object_label, int request) } } } + rcu_read_unlock(); /* * This is a bit map operation. */ @@ -228,14 +221,17 @@ struct smack_known *smk_import_entry(const char *string, int len) mutex_lock(&smack_known_lock); - for (skp = smack_known; skp != NULL; skp = skp->smk_next) - if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) + found = 0; + list_for_each_entry_rcu(skp, &smack_known_list, list) { + if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) { + found = 1; break; + } + } - if (skp == NULL) { + if (found == 0) { skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL); if (skp != NULL) { - skp->smk_next = smack_known; strncpy(skp->smk_known, smack, SMK_MAXLEN); skp->smk_secid = smack_next_secid++; skp->smk_cipso = NULL; @@ -244,8 +240,7 @@ struct smack_known *smk_import_entry(const char *string, int len) * Make sure that the entry is actually * filled before putting it on the list. */ - smp_mb(); - smack_known = skp; + list_add_rcu(&skp->list, &smack_known_list); } } @@ -283,14 +278,19 @@ char *smack_from_secid(const u32 secid) { struct smack_known *skp; - for (skp = smack_known; skp != NULL; skp = skp->smk_next) - if (skp->smk_secid == secid) + rcu_read_lock(); + list_for_each_entry_rcu(skp, &smack_known_list, list) { + if (skp->smk_secid == secid) { + rcu_read_unlock(); return skp->smk_known; + } + } /* * If we got this far someone asked for the translation * of a secid that is not on the list. */ + rcu_read_unlock(); return smack_known_invalid.smk_known; } @@ -305,9 +305,14 @@ u32 smack_to_secid(const char *smack) { struct smack_known *skp; - for (skp = smack_known; skp != NULL; skp = skp->smk_next) - if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) + rcu_read_lock(); + list_for_each_entry_rcu(skp, &smack_known_list, list) { + if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) { + rcu_read_unlock(); return skp->smk_secid; + } + } + rcu_read_unlock(); return 0; } @@ -332,7 +337,8 @@ void smack_from_cipso(u32 level, char *cp, char *result) struct smack_known *kp; char *final = NULL; - for (kp = smack_known; final == NULL && kp != NULL; kp = kp->smk_next) { + rcu_read_lock(); + list_for_each_entry(kp, &smack_known_list, list) { if (kp->smk_cipso == NULL) continue; @@ -344,6 +350,7 @@ void smack_from_cipso(u32 level, char *cp, char *result) spin_unlock_bh(&kp->smk_cipsolock); } + rcu_read_unlock(); if (final == NULL) final = smack_known_huh.smk_known; strncpy(result, final, SMK_MAXLEN); @@ -360,13 +367,19 @@ void smack_from_cipso(u32 level, char *cp, char *result) int smack_to_cipso(const char *smack, struct smack_cipso *cp) { struct smack_known *kp; + int found = 0; - for (kp = smack_known; kp != NULL; kp = kp->smk_next) + rcu_read_lock(); + list_for_each_entry_rcu(kp, &smack_known_list, list) { if (kp->smk_known == smack || - strcmp(kp->smk_known, smack) == 0) + strcmp(kp->smk_known, smack) == 0) { + found = 1; break; + } + } + rcu_read_unlock(); - if (kp == NULL || kp->smk_cipso == NULL) + if (found == 0 || kp->smk_cipso == NULL) return -ENOENT; memcpy(cp, kp->smk_cipso, sizeof(struct smack_cipso)); diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 84b62b5e9e2c..fd20d15f5b9a 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1508,7 +1508,8 @@ static char *smack_host_label(struct sockaddr_in *sip) if (siap->s_addr == 0) return NULL; - for (snp = smack_netlbladdrs; snp != NULL; snp = snp->smk_next) { + rcu_read_lock(); + list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list) { /* * we break after finding the first match because * the list is sorted from longest to shortest mask @@ -1516,10 +1517,11 @@ static char *smack_host_label(struct sockaddr_in *sip) */ if ((&snp->smk_host.sin_addr)->s_addr == (siap->s_addr & (&snp->smk_mask)->s_addr)) { + rcu_read_unlock(); return snp->smk_label; } } - + rcu_read_unlock(); return NULL; } @@ -2930,6 +2932,17 @@ struct security_operations smack_ops = { .release_secctx = smack_release_secctx, }; + +static __init void init_smack_know_list(void) +{ + list_add(&smack_known_huh.list, &smack_known_list); + list_add(&smack_known_hat.list, &smack_known_list); + list_add(&smack_known_star.list, &smack_known_list); + list_add(&smack_known_floor.list, &smack_known_list); + list_add(&smack_known_invalid.list, &smack_known_list); + list_add(&smack_known_web.list, &smack_known_list); +} + /** * smack_init - initialize the smack system * @@ -2950,6 +2963,8 @@ static __init int smack_init(void) cred = (struct cred *) current->cred; cred->security = &smack_known_floor.smk_known; + /* initilize the smack_know_list */ + init_smack_know_list(); /* * Initialize locks */ diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index a1b57e4dba3e..856c8a287523 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -80,10 +80,11 @@ char *smack_onlycap; * Packets are sent there unlabeled, but only from tasks that * can write to the specified label. */ -struct smk_netlbladdr *smack_netlbladdrs; + +LIST_HEAD(smk_netlbladdr_list); +LIST_HEAD(smack_rule_list); static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; -struct smk_list_entry *smack_list; #define SEQ_READ_FINISHED 1 @@ -134,24 +135,27 @@ static void *load_seq_start(struct seq_file *s, loff_t *pos) { if (*pos == SEQ_READ_FINISHED) return NULL; - - return smack_list; + if (list_empty(&smack_rule_list)) + return NULL; + return smack_rule_list.next; } static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct smk_list_entry *skp = ((struct smk_list_entry *) v)->smk_next; + struct list_head *list = v; - if (skp == NULL) + if (list_is_last(list, &smack_rule_list)) { *pos = SEQ_READ_FINISHED; - - return skp; + return NULL; + } + return list->next; } static int load_seq_show(struct seq_file *s, void *v) { - struct smk_list_entry *slp = (struct smk_list_entry *) v; - struct smack_rule *srp = &slp->smk_rule; + struct list_head *list = v; + struct smack_rule *srp = + list_entry(list, struct smack_rule, list); seq_printf(s, "%s %s", (char *)srp->smk_subject, (char *)srp->smk_object); @@ -212,32 +216,23 @@ static int smk_open_load(struct inode *inode, struct file *file) */ static int smk_set_access(struct smack_rule *srp) { - struct smk_list_entry *sp; - struct smk_list_entry *newp; + struct smack_rule *sp; int ret = 0; - + int found; mutex_lock(&smack_list_lock); - for (sp = smack_list; sp != NULL; sp = sp->smk_next) - if (sp->smk_rule.smk_subject == srp->smk_subject && - sp->smk_rule.smk_object == srp->smk_object) { - sp->smk_rule.smk_access = srp->smk_access; + found = 0; + list_for_each_entry_rcu(sp, &smack_rule_list, list) { + if (sp->smk_subject == srp->smk_subject && + sp->smk_object == srp->smk_object) { + found = 1; + sp->smk_access = srp->smk_access; break; } - - if (sp == NULL) { - newp = kzalloc(sizeof(struct smk_list_entry), GFP_KERNEL); - if (newp == NULL) { - ret = -ENOMEM; - goto out; - } - - newp->smk_rule = *srp; - newp->smk_next = smack_list; - smack_list = newp; } + if (found == 0) + list_add_rcu(&srp->list, &smack_rule_list); -out: mutex_unlock(&smack_list_lock); return ret; @@ -261,7 +256,7 @@ out: static ssize_t smk_write_load(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct smack_rule rule; + struct smack_rule *rule; char *data; int rc = -EINVAL; @@ -272,9 +267,8 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, */ if (!capable(CAP_MAC_ADMIN)) return -EPERM; - if (*ppos != 0) - return -EINVAL; - if (count != SMK_LOADLEN) + + if (*ppos != 0 || count != SMK_LOADLEN) return -EINVAL; data = kzalloc(count, GFP_KERNEL); @@ -286,25 +280,31 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, goto out; } - rule.smk_subject = smk_import(data, 0); - if (rule.smk_subject == NULL) + rule = kzalloc(sizeof(*rule), GFP_KERNEL); + if (rule == NULL) { + rc = -ENOMEM; goto out; + } - rule.smk_object = smk_import(data + SMK_LABELLEN, 0); - if (rule.smk_object == NULL) - goto out; + rule->smk_subject = smk_import(data, 0); + if (rule->smk_subject == NULL) + goto out_free_rule; - rule.smk_access = 0; + rule->smk_object = smk_import(data + SMK_LABELLEN, 0); + if (rule->smk_object == NULL) + 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; + rule->smk_access |= MAY_READ; break; default: - goto out; + goto out_free_rule; } switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) { @@ -312,10 +312,10 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, break; case 'w': case 'W': - rule.smk_access |= MAY_WRITE; + rule->smk_access |= MAY_WRITE; break; default: - goto out; + goto out_free_rule; } switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) { @@ -323,10 +323,10 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, break; case 'x': case 'X': - rule.smk_access |= MAY_EXEC; + rule->smk_access |= MAY_EXEC; break; default: - goto out; + goto out_free_rule; } switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) { @@ -334,17 +334,20 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, break; case 'a': case 'A': - rule.smk_access |= MAY_APPEND; + rule->smk_access |= MAY_APPEND; break; default: - goto out; + goto out_free_rule; } - rc = smk_set_access(&rule); + rc = smk_set_access(rule); if (!rc) rc = count; + goto out; +out_free_rule: + kfree(rule); out: kfree(data); return rc; @@ -433,24 +436,26 @@ static void *cipso_seq_start(struct seq_file *s, loff_t *pos) { if (*pos == SEQ_READ_FINISHED) return NULL; + if (list_empty(&smack_known_list)) + return NULL; - return smack_known; + return smack_known_list.next; } static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct smack_known *skp = ((struct smack_known *) v)->smk_next; + struct list_head *list = v; /* - * Omit labels with no associated cipso value + * labels with no associated cipso value wont be printed + * in cipso_seq_show */ - while (skp != NULL && !skp->smk_cipso) - skp = skp->smk_next; - - if (skp == NULL) + if (list_is_last(list, &smack_known_list)) { *pos = SEQ_READ_FINISHED; + return NULL; + } - return skp; + return list->next; } /* @@ -459,7 +464,9 @@ static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos) */ static int cipso_seq_show(struct seq_file *s, void *v) { - struct smack_known *skp = (struct smack_known *) v; + struct list_head *list = v; + struct smack_known *skp = + list_entry(list, struct smack_known, list); struct smack_cipso *scp = skp->smk_cipso; char *cbp; char sep = '/'; @@ -638,18 +645,21 @@ static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos) { if (*pos == SEQ_READ_FINISHED) return NULL; - - return smack_netlbladdrs; + if (list_empty(&smk_netlbladdr_list)) + return NULL; + return smk_netlbladdr_list.next; } static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct smk_netlbladdr *skp = ((struct smk_netlbladdr *) v)->smk_next; + struct list_head *list = v; - if (skp == NULL) + if (list_is_last(list, &smk_netlbladdr_list)) { *pos = SEQ_READ_FINISHED; + return NULL; + } - return skp; + return list->next; } #define BEBITS (sizeof(__be32) * 8) @@ -658,7 +668,9 @@ static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos) */ static int netlbladdr_seq_show(struct seq_file *s, void *v) { - struct smk_netlbladdr *skp = (struct smk_netlbladdr *) v; + struct list_head *list = v; + struct smk_netlbladdr *skp = + list_entry(list, struct smk_netlbladdr, list); unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr; int maskn; u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr); @@ -702,30 +714,36 @@ static int smk_open_netlbladdr(struct inode *inode, struct file *file) * * This helper insert netlabel in the smack_netlbladdrs list * sorted by netmask length (longest to smallest) + * locked by &smk_netlbladdr_lock in smk_write_netlbladdr + * */ static void smk_netlbladdr_insert(struct smk_netlbladdr *new) { - struct smk_netlbladdr *m; + struct smk_netlbladdr *m, *m_next; - if (smack_netlbladdrs == NULL) { - smack_netlbladdrs = new; + if (list_empty(&smk_netlbladdr_list)) { + list_add_rcu(&new->list, &smk_netlbladdr_list); return; } + m = list_entry(rcu_dereference(smk_netlbladdr_list.next), + struct smk_netlbladdr, list); + /* the comparison '>' is a bit hacky, but works */ - if (new->smk_mask.s_addr > smack_netlbladdrs->smk_mask.s_addr) { - new->smk_next = smack_netlbladdrs; - smack_netlbladdrs = new; + if (new->smk_mask.s_addr > m->smk_mask.s_addr) { + list_add_rcu(&new->list, &smk_netlbladdr_list); return; } - for (m = smack_netlbladdrs; m != NULL; m = m->smk_next) { - if (m->smk_next == NULL) { - m->smk_next = new; + + list_for_each_entry_rcu(m, &smk_netlbladdr_list, list) { + if (list_is_last(&m->list, &smk_netlbladdr_list)) { + list_add_rcu(&new->list, &m->list); return; } - if (new->smk_mask.s_addr > m->smk_next->smk_mask.s_addr) { - new->smk_next = m->smk_next; - m->smk_next = new; + m_next = list_entry(rcu_dereference(m->list.next), + struct smk_netlbladdr, list); + if (new->smk_mask.s_addr > m_next->smk_mask.s_addr) { + list_add_rcu(&new->list, &m->list); return; } } @@ -755,6 +773,7 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, struct netlbl_audit audit_info; struct in_addr mask; unsigned int m; + int found; u32 mask_bits = (1<<31); __be32 nsa; u32 temp_mask; @@ -808,14 +827,17 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, nsa = newname.sin_addr.s_addr; /* try to find if the prefix is already in the list */ - for (skp = smack_netlbladdrs; skp != NULL; skp = skp->smk_next) + found = 0; + list_for_each_entry_rcu(skp, &smk_netlbladdr_list, list) { if (skp->smk_host.sin_addr.s_addr == nsa && - skp->smk_mask.s_addr == mask.s_addr) + skp->smk_mask.s_addr == mask.s_addr) { + found = 1; break; - + } + } smk_netlabel_audit_set(&audit_info); - if (skp == NULL) { + if (found == 0) { skp = kzalloc(sizeof(*skp), GFP_KERNEL); if (skp == NULL) rc = -ENOMEM; |