From 921f3ac4c3f2fd46ae99195a1168383ca9b41ed1 Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Sat, 16 Mar 2013 00:50:53 +0800 Subject: [PATCH 1/6] tomoyo: use DEFINE_SRCU() to define tomoyo_ss DEFINE_STATIC_SRCU() defines srcu struct and do init at build time. Signed-off-by: Lai Jiangshan Acked-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/tomoyo.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index a2ee362546ab..f0b756e27fed 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -536,7 +536,7 @@ static struct security_operations tomoyo_security_ops = { }; /* Lock for GC. */ -struct srcu_struct tomoyo_ss; +DEFINE_SRCU(tomoyo_ss); /** * tomoyo_init - Register TOMOYO Linux as a LSM module. @@ -550,8 +550,7 @@ static int __init tomoyo_init(void) if (!security_module_enable(&tomoyo_security_ops)) return 0; /* register ourselves with the security framework */ - if (register_security(&tomoyo_security_ops) || - init_srcu_struct(&tomoyo_ss)) + if (register_security(&tomoyo_security_ops)) panic("Failure registering TOMOYO Linux"); printk(KERN_INFO "TOMOYO Linux initialized\n"); cred->security = &tomoyo_kernel_domain; From d15d9fad16f6aa459cf4926a1d3aba36b004e9a2 Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Tue, 27 Nov 2012 16:28:11 +0100 Subject: [PATCH 2/6] Smack: prevent revoke-subject from failing when unseen label is written to it Special file /smack/revoke-subject will silently accept labels that are not present on the subject label list. Nothing has to be done for such labels, as there are no rules for them to revoke. Targeted for git://git.gitorious.org/smack-next/kernel.git Signed-off-by: Rafal Krypa --- security/smack/smackfs.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 76a5dca46404..337e32c551da 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -2035,10 +2035,8 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf, } skp = smk_find_entry(cp); - if (skp == NULL) { - rc = -EINVAL; + if (skp == NULL) goto free_out; - } rule_list = &skp->smk_rules; rule_lock = &skp->smk_rules_lock; From a87d79ad7cfa299aa14bb22758313dec33909875 Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Tue, 27 Nov 2012 16:29:07 +0100 Subject: [PATCH 3/6] Smack: add missing support for transmute bit in smack_str_from_perm() This fixes audit logs for granting or denial of permissions to show information about transmute bit. Targeted for git://git.gitorious.org/smack-next/kernel.git Signed-off-by: Rafal Krypa --- security/smack/smack_access.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index db14689a21e0..2e397a88d410 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -252,6 +252,8 @@ static inline void smack_str_from_perm(char *string, int access) string[i++] = 'x'; if (access & MAY_APPEND) string[i++] = 'a'; + if (access & MAY_TRANSMUTE) + string[i++] = 't'; string[i] = '\0'; } /** From cee7e443344a3845e5b9111614b41e0b1afb60ce Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Tue, 6 Nov 2012 10:17:49 +0200 Subject: [PATCH 4/6] smack: SMACK_MAGIC to include/uapi/linux/magic.h SMACK_MAGIC moved to a proper place for easy user space access (i.e. libsmack). Signed-off-by: Jarkko Sakkinen --- include/uapi/linux/magic.h | 1 + security/smack/smack.h | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h index 873e086ce3a1..249df3720be2 100644 --- a/include/uapi/linux/magic.h +++ b/include/uapi/linux/magic.h @@ -11,6 +11,7 @@ #define DEBUGFS_MAGIC 0x64626720 #define SECURITYFS_MAGIC 0x73636673 #define SELINUX_MAGIC 0xf97cff8c +#define SMACK_MAGIC 0x43415d53 /* "SMAC" */ #define RAMFS_MAGIC 0x858458f6 /* some random number */ #define TMPFS_MAGIC 0x01021994 #define HUGETLBFS_MAGIC 0x958458f6 /* some random number */ diff --git a/security/smack/smack.h b/security/smack/smack.h index 99b36124f712..8ad30955e15d 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -148,11 +148,6 @@ struct smack_known { #define SMACK_UNLABELED_SOCKET 0 #define SMACK_CIPSO_SOCKET 1 -/* - * smackfs magic number - */ -#define SMACK_MAGIC 0x43415d53 /* "SMAC" */ - /* * CIPSO defaults. */ From e05b6f982a049113a88a1750e13fdb15298cbed4 Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Thu, 10 Jan 2013 19:42:00 +0100 Subject: [PATCH 5/6] Smack: add support for modification of existing rules Rule modifications are enabled via /smack/change-rule. Format is as follows: "Subject Object rwaxt rwaxt" First two strings are subject and object labels up to 255 characters. Third string contains permissions to enable. Fourth string contains permissions to disable. All unmentioned permissions will be left unchanged. If no rule previously existed, it will be created. Targeted for git://git.gitorious.org/smack-next/kernel.git Signed-off-by: Rafal Krypa --- Documentation/security/Smack.txt | 11 ++ security/smack/smackfs.c | 247 +++++++++++++++++++++---------- 2 files changed, 180 insertions(+), 78 deletions(-) diff --git a/Documentation/security/Smack.txt b/Documentation/security/Smack.txt index 8a177e4b6e21..7a2d30c132e3 100644 --- a/Documentation/security/Smack.txt +++ b/Documentation/security/Smack.txt @@ -117,6 +117,17 @@ access2 ambient This contains the Smack label applied to unlabeled network packets. +change-rule + This interface allows modification of existing access control rules. + The format accepted on write is: + "%s %s %s %s" + where the first string is the subject label, the second the + object label, the third the access to allow and the fourth the + access to deny. The access strings may contain only the characters + "rwxat-". If a rule for a given subject and object exists it will be + modified by enabling the permissions in the third string and disabling + those in the fourth string. If there is no such rule it will be + created using the access specified in the third and the fourth strings. cipso This interface allows a specific CIPSO header to be assigned to a Smack label. The format accepted on write is: diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 337e32c551da..2479a41a7dff 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -50,12 +50,12 @@ enum smk_inos { SMK_ACCESS2 = 16, /* make an access check with long labels */ SMK_CIPSO2 = 17, /* load long label -> CIPSO mapping */ SMK_REVOKE_SUBJ = 18, /* set rules with subject label to '-' */ + SMK_CHANGE_RULE = 19, /* change or add rules (long labels) */ }; /* * List locks */ -static DEFINE_MUTEX(smack_list_lock); static DEFINE_MUTEX(smack_cipso_lock); static DEFINE_MUTEX(smack_ambient_lock); static DEFINE_MUTEX(smk_netlbladdr_lock); @@ -110,6 +110,13 @@ struct smack_master_list { LIST_HEAD(smack_rule_list); +struct smack_parsed_rule { + char *smk_subject; + char *smk_object; + int smk_access1; + int smk_access2; +}; + static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; const char *smack_cipso_option = SMACK_CIPSO_OPTION; @@ -167,25 +174,28 @@ static void smk_netlabel_audit_set(struct netlbl_audit *nap) #define SMK_NETLBLADDRMIN 9 /** - * smk_set_access - add a rule to the rule list - * @srp: the new rule to add + * smk_set_access - add a rule to the rule list or replace an old rule + * @srp: the rule to add or replace * @rule_list: the list of rules * @rule_lock: the rule list lock + * @global: if non-zero, indicates a global rule * * Looks through the current subject/object/access list for * the subject/object pair and replaces the access that was * there. If the pair isn't found add it with the specified * access. * - * Returns 1 if a rule was found to exist already, 0 if it is new * Returns 0 if nothing goes wrong or -ENOMEM if it fails * during the allocation of the new pair to add. */ -static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, - struct mutex *rule_lock) +static int smk_set_access(struct smack_parsed_rule *srp, + struct list_head *rule_list, + struct mutex *rule_lock, int global) { struct smack_rule *sp; + struct smack_master_list *smlp; int found = 0; + int rc = 0; mutex_lock(rule_lock); @@ -197,23 +207,89 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, if (sp->smk_object == srp->smk_object && sp->smk_subject == srp->smk_subject) { found = 1; - sp->smk_access = srp->smk_access; + sp->smk_access |= srp->smk_access1; + sp->smk_access &= ~srp->smk_access2; break; } } - if (found == 0) - list_add_rcu(&srp->list, rule_list); + if (found == 0) { + sp = kzalloc(sizeof(*sp), GFP_KERNEL); + if (sp == NULL) { + rc = -ENOMEM; + goto out; + } + + sp->smk_subject = srp->smk_subject; + sp->smk_object = srp->smk_object; + sp->smk_access = srp->smk_access1 & ~srp->smk_access2; + + list_add_rcu(&sp->list, rule_list); + /* + * If this is a global as opposed to self and a new rule + * it needs to get added for reporting. + */ + if (global) { + smlp = kzalloc(sizeof(*smlp), GFP_KERNEL); + if (smlp != NULL) { + smlp->smk_rule = sp; + list_add_rcu(&smlp->list, &smack_rule_list); + } else + rc = -ENOMEM; + } + } + +out: mutex_unlock(rule_lock); + return rc; +} - return found; +/** + * smk_perm_from_str - parse smack accesses from a text string + * @string: a text string that contains a Smack accesses code + * + * Returns an integer with respective bits set for specified accesses. + */ +static int smk_perm_from_str(const char *string) +{ + int perm = 0; + const char *cp; + + for (cp = string; ; cp++) + switch (*cp) { + case '-': + break; + case 'r': + case 'R': + perm |= MAY_READ; + break; + case 'w': + case 'W': + perm |= MAY_WRITE; + break; + case 'x': + case 'X': + perm |= MAY_EXEC; + break; + case 'a': + case 'A': + perm |= MAY_APPEND; + break; + case 't': + case 'T': + perm |= MAY_TRANSMUTE; + break; + default: + return perm; + } } /** * smk_fill_rule - Fill Smack rule from strings * @subject: subject label string * @object: object label string - * @access: access string + * @access1: access string + * @access2: string with permissions to be removed * @rule: Smack rule * @import: if non-zero, import labels * @len: label length limit @@ -221,8 +297,9 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, * Returns 0 on success, -1 on failure */ static int smk_fill_rule(const char *subject, const char *object, - const char *access, struct smack_rule *rule, - int import, int len) + const char *access1, const char *access2, + struct smack_parsed_rule *rule, int import, + int len) { const char *cp; struct smack_known *skp; @@ -255,36 +332,11 @@ static int smk_fill_rule(const char *subject, const char *object, rule->smk_object = skp->smk_known; } - rule->smk_access = 0; - - for (cp = access; *cp != '\0'; cp++) { - switch (*cp) { - case '-': - break; - case 'r': - case 'R': - rule->smk_access |= MAY_READ; - break; - case 'w': - case 'W': - rule->smk_access |= MAY_WRITE; - break; - case 'x': - case 'X': - rule->smk_access |= MAY_EXEC; - break; - case 'a': - case 'A': - rule->smk_access |= MAY_APPEND; - break; - case 't': - case 'T': - rule->smk_access |= MAY_TRANSMUTE; - break; - default: - return 0; - } - } + rule->smk_access1 = smk_perm_from_str(access1); + if (access2) + rule->smk_access2 = smk_perm_from_str(access2); + else + rule->smk_access2 = ~rule->smk_access1; return 0; } @@ -297,30 +349,33 @@ static int smk_fill_rule(const char *subject, const char *object, * * Returns 0 on success, -1 on errors. */ -static int smk_parse_rule(const char *data, struct smack_rule *rule, int import) +static int smk_parse_rule(const char *data, struct smack_parsed_rule *rule, + int import) { int rc; rc = smk_fill_rule(data, data + SMK_LABELLEN, - data + SMK_LABELLEN + SMK_LABELLEN, rule, import, - SMK_LABELLEN); + data + SMK_LABELLEN + SMK_LABELLEN, NULL, rule, + import, SMK_LABELLEN); return rc; } /** * smk_parse_long_rule - parse Smack rule from rule string * @data: string to be parsed, null terminated - * @rule: Smack rule + * @rule: Will be filled with Smack parsed rule * @import: if non-zero, import labels + * @change: if non-zero, data is from /smack/change-rule * * Returns 0 on success, -1 on failure */ -static int smk_parse_long_rule(const char *data, struct smack_rule *rule, - int import) +static int smk_parse_long_rule(const char *data, struct smack_parsed_rule *rule, + int import, int change) { char *subject; char *object; - char *access; + char *access1; + char *access2; int datalen; int rc = -1; @@ -334,14 +389,27 @@ static int smk_parse_long_rule(const char *data, struct smack_rule *rule, object = kzalloc(datalen, GFP_KERNEL); if (object == NULL) goto free_out_s; - access = kzalloc(datalen, GFP_KERNEL); - if (access == NULL) + access1 = kzalloc(datalen, GFP_KERNEL); + if (access1 == NULL) goto free_out_o; + access2 = kzalloc(datalen, GFP_KERNEL); + if (access2 == NULL) + goto free_out_a; - if (sscanf(data, "%s %s %s", subject, object, access) == 3) - rc = smk_fill_rule(subject, object, access, rule, import, 0); + if (change) { + if (sscanf(data, "%s %s %s %s", + subject, object, access1, access2) == 4) + rc = smk_fill_rule(subject, object, access1, access2, + rule, import, 0); + } else { + if (sscanf(data, "%s %s %s", subject, object, access1) == 3) + rc = smk_fill_rule(subject, object, access1, NULL, + rule, import, 0); + } - kfree(access); + kfree(access2); +free_out_a: + kfree(access1); free_out_o: kfree(object); free_out_s: @@ -351,6 +419,7 @@ free_out_s: #define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */ #define SMK_LONG_FMT 1 /* Variable long label format */ +#define SMK_CHANGE_FMT 2 /* Rule modification format */ /** * smk_write_rules_list - write() for any /smack rule file * @file: file pointer, not actually used @@ -359,22 +428,24 @@ free_out_s: * @ppos: where to start - must be 0 * @rule_list: the list of rules to write to * @rule_lock: lock for the rule list - * @format: /smack/load or /smack/load2 format. + * @format: /smack/load or /smack/load2 or /smack/change-rule format. * * Get one smack access rule from above. * The format for SMK_LONG_FMT is: * "subjectobjectaccess[...]" * The format for SMK_FIXED24_FMT is exactly: * "subject object rwxat" + * The format for SMK_CHANGE_FMT is: + * "subjectobject + * acc_enableacc_disable[...]" */ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, size_t count, loff_t *ppos, struct list_head *rule_list, struct mutex *rule_lock, int format) { - struct smack_master_list *smlp; struct smack_known *skp; - struct smack_rule *rule; + struct smack_parsed_rule *rule; char *data; int datalen; int rc = -EINVAL; @@ -417,7 +488,11 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, * Be sure the data string is terminated. */ data[count] = '\0'; - if (smk_parse_long_rule(data, rule, 1)) + if (smk_parse_long_rule(data, rule, 1, 0)) + goto out_free_rule; + } else if (format == SMK_CHANGE_FMT) { + data[count] = '\0'; + if (smk_parse_long_rule(data, rule, 1, 1)) goto out_free_rule; } else { /* @@ -437,22 +512,9 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, rule_lock = &skp->smk_rules_lock; } - rc = count; - /* - * If this is a global as opposed to self and a new rule - * it needs to get added for reporting. - * 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 (load) { - smlp = kzalloc(sizeof(*smlp), GFP_KERNEL); - if (smlp != NULL) { - smlp->smk_rule = rule; - list_add_rcu(&smlp->list, &smack_rule_list); - } else - rc = -ENOMEM; - } + rc = smk_set_access(rule, rule_list, rule_lock, load); + if (rc == 0) { + rc = count; goto out; } @@ -1774,7 +1836,7 @@ static const struct file_operations smk_load_self_ops = { static ssize_t smk_user_access(struct file *file, const char __user *buf, size_t count, loff_t *ppos, int format) { - struct smack_rule rule; + struct smack_parsed_rule rule; char *data; char *cod; int res; @@ -1796,14 +1858,14 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, return -ENOMEM; memcpy(cod, data, count); cod[count] = '\0'; - res = smk_parse_long_rule(cod, &rule, 0); + res = smk_parse_long_rule(cod, &rule, 0, 0); kfree(cod); } if (res) return -EINVAL; - res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access, + res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access1, NULL); data[0] = res == 0 ? '1' : '0'; data[1] = '\0'; @@ -2074,6 +2136,33 @@ static int smk_init_sysfs(void) return 0; } +/** + * smk_write_change_rule - write() for /smack/change-rule + * @file: file pointer + * @buf: data from user space + * @count: bytes sent + * @ppos: where to start - must be 0 + */ +static ssize_t smk_write_change_rule(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + /* + * Must have privilege. + */ + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + + return smk_write_rules_list(file, buf, count, ppos, NULL, NULL, + SMK_CHANGE_FMT); +} + +static const struct file_operations smk_change_rule_ops = { + .write = smk_write_change_rule, + .read = simple_transaction_read, + .release = simple_transaction_release, + .llseek = generic_file_llseek, +}; + /** * smk_fill_super - fill the /smackfs superblock * @sb: the empty superblock @@ -2123,6 +2212,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) [SMK_REVOKE_SUBJ] = { "revoke-subject", &smk_revoke_subj_ops, S_IRUGO|S_IWUSR}, + [SMK_CHANGE_RULE] = { + "change-rule", &smk_change_rule_ops, S_IRUGO|S_IWUSR}, /* last one */ {""} }; From cdb56b60884c687ea396ae96a418554739b40129 Mon Sep 17 00:00:00 2001 From: Igor Zhbanov Date: Tue, 19 Mar 2013 13:49:47 +0400 Subject: [PATCH 6/6] Fix NULL pointer dereference in smack_inode_unlink() and smack_inode_rmdir() This patch fixes kernel Oops because of wrong common_audit_data type in smack_inode_unlink() and smack_inode_rmdir(). When SMACK security module is enabled and SMACK logging is on (/smack/logging is not zero) and you try to delete the file which 1) you cannot delete due to SMACK rules and logging of failures is on or 2) you can delete and logging of success is on, you will see following: Unable to handle kernel NULL pointer dereference at virtual address 000002d7 [<...>] (strlen+0x0/0x28) [<...>] (audit_log_untrustedstring+0x14/0x28) [<...>] (common_lsm_audit+0x108/0x6ac) [<...>] (smack_log+0xc4/0xe4) [<...>] (smk_curacc+0x80/0x10c) [<...>] (smack_inode_unlink+0x74/0x80) [<...>] (security_inode_unlink+0x2c/0x30) [<...>] (vfs_unlink+0x7c/0x100) [<...>] (do_unlinkat+0x144/0x16c) The function smack_inode_unlink() (and smack_inode_rmdir()) need to log two structures of different types. First of all it does: smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); This will set common audit data type to LSM_AUDIT_DATA_DENTRY and store dentry for auditing (by function smk_curacc(), which in turn calls dump_common_audit_data(), which is actually uses provided data and logs it). /* * You need write access to the thing you're unlinking */ rc = smk_curacc(smk_of_inode(ip), MAY_WRITE, &ad); if (rc == 0) { /* * You also need write access to the containing directory */ Then this function wants to log anoter data: smk_ad_setfield_u_fs_path_dentry(&ad, NULL); smk_ad_setfield_u_fs_inode(&ad, dir); The function sets inode field, but don't change common_audit_data type. rc = smk_curacc(smk_of_inode(dir), MAY_WRITE, &ad); } So the dump_common_audit() function incorrectly interprets inode structure as dentry, and Oops will happen. This patch reinitializes common_audit_data structures with correct type. Also I removed unneeded smk_ad_setfield_u_fs_path_dentry(&ad, NULL); initialization, because both dentry and inode pointers are stored in the same union. Signed-off-by: Igor Zhbanov Signed-off-by: Kyungmin Park --- security/smack/smack_lsm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index fa64740abb59..d52c780bdb78 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -654,7 +654,7 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) /* * You also need write access to the containing directory */ - smk_ad_setfield_u_fs_path_dentry(&ad, NULL); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE); smk_ad_setfield_u_fs_inode(&ad, dir); rc = smk_curacc(smk_of_inode(dir), MAY_WRITE, &ad); } @@ -685,7 +685,7 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) /* * You also need write access to the containing directory */ - smk_ad_setfield_u_fs_path_dentry(&ad, NULL); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE); smk_ad_setfield_u_fs_inode(&ad, dir); rc = smk_curacc(smk_of_inode(dir), MAY_WRITE, &ad); }