From 921f3ac4c3f2fd46ae99195a1168383ca9b41ed1 Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Sat, 16 Mar 2013 00:50:53 +0800 Subject: 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 diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index a2ee362..f0b756e 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; -- cgit v0.10.2 From 505f14f7b8d446b8e4bc2a6cfc723afbbb365f65 Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Sat, 16 Mar 2013 00:50:53 +0800 Subject: 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 diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index a2ee362..f0b756e 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; -- cgit v0.10.2 From d15d9fad16f6aa459cf4926a1d3aba36b004e9a2 Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Tue, 27 Nov 2012 16:28:11 +0100 Subject: 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 diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 76a5dca..337e32c 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; -- cgit v0.10.2 From a87d79ad7cfa299aa14bb22758313dec33909875 Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Tue, 27 Nov 2012 16:29:07 +0100 Subject: 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 diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index db14689..2e397a8 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'; } /** -- cgit v0.10.2 From cee7e443344a3845e5b9111614b41e0b1afb60ce Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Tue, 6 Nov 2012 10:17:49 +0200 Subject: 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 diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h index 873e086..249df37 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 99b3612..8ad3095 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -149,11 +149,6 @@ struct smack_known { #define SMACK_CIPSO_SOCKET 1 /* - * smackfs magic number - */ -#define SMACK_MAGIC 0x43415d53 /* "SMAC" */ - -/* * CIPSO defaults. */ #define SMACK_CIPSO_DOI_DEFAULT 3 /* Historical */ -- cgit v0.10.2 From e05b6f982a049113a88a1750e13fdb15298cbed4 Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Thu, 10 Jan 2013 19:42:00 +0100 Subject: 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 diff --git a/Documentation/security/Smack.txt b/Documentation/security/Smack.txt index 8a177e4..7a2d30c 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 337e32c..2479a41 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; +} + +/** + * 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; - return found; + 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 (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); + } - if (sscanf(data, "%s %s %s", subject, object, access) == 3) - rc = smk_fill_rule(subject, object, access, 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'; @@ -2075,6 +2137,33 @@ static int smk_init_sysfs(void) } /** + * 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 * @data: unused @@ -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 */ {""} }; -- cgit v0.10.2 From cdb56b60884c687ea396ae96a418554739b40129 Mon Sep 17 00:00:00 2001 From: Igor Zhbanov Date: Tue, 19 Mar 2013 13:49:47 +0400 Subject: 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 diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index fa64740..d52c780 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); } -- cgit v0.10.2 From d13274794ad28d90ac81643d3d1d18338d948764 Mon Sep 17 00:00:00 2001 From: Nicolas Schichan Date: Fri, 15 Mar 2013 18:02:00 +0100 Subject: seccomp: allow BPF_XOR based ALU instructions. Allow BPF_XOR based ALU instructions. Signed-off-by: Nicolas Schichan Acked-by: Kees Cook Acked-by: Will Drewry Signed-off-by: James Morris diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 5af44b5..b7a1004 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -160,6 +160,8 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen) case BPF_S_ALU_AND_X: case BPF_S_ALU_OR_K: case BPF_S_ALU_OR_X: + case BPF_S_ALU_XOR_K: + case BPF_S_ALU_XOR_X: case BPF_S_ALU_LSH_K: case BPF_S_ALU_LSH_X: case BPF_S_ALU_RSH_K: -- cgit v0.10.2 From 094f7b69ea738d7d619cba449d2af97159949459 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 1 Apr 2013 08:14:24 -0400 Subject: selinux: make security_sb_clone_mnt_opts return an error on context mismatch I had the following problem reported a while back. If you mount the same filesystem twice using NFSv4 with different contexts, then the second context= option is ignored. For instance: # mount server:/export /mnt/test1 # mount server:/export /mnt/test2 -o context=system_u:object_r:tmp_t:s0 # ls -dZ /mnt/test1 drwxrwxrwt. root root system_u:object_r:nfs_t:s0 /mnt/test1 # ls -dZ /mnt/test2 drwxrwxrwt. root root system_u:object_r:nfs_t:s0 /mnt/test2 When we call into SELinux to set the context of a "cloned" superblock, it will currently just bail out when it notices that we're reusing an existing superblock. Since the existing superblock is already set up and presumably in use, we can't go overwriting its context with the one from the "original" sb. Because of this, the second context= option in this case cannot take effect. This patch fixes this by turning security_sb_clone_mnt_opts into an int return operation. When it finds that the "new" superblock that it has been handed is already set up, it checks to see whether the contexts on the old superblock match it. If it does, then it will just return success, otherwise it'll return -EBUSY and emit a printk to tell the admin why the second mount failed. Note that this patch may cause casualties. The NFSv4 code relies on being able to walk down to an export from the pseudoroot. If you mount filesystems that are nested within one another with different contexts, then this patch will make those mounts fail in new and "exciting" ways. For instance, suppose that /export is a separate filesystem on the server: # mount server:/ /mnt/test1 # mount salusa:/export /mnt/test2 -o context=system_u:object_r:tmp_t:s0 mount.nfs: an incorrect mount option was specified ...with the printk in the ring buffer. Because we *might* eventually walk down to /mnt/test1/export, the mount is denied due to this patch. The second mount needs the pseudoroot superblock, but that's already present with the wrong context. OTOH, if we mount these in the reverse order, then both mounts work, because the pseudoroot superblock created when mounting /export is discarded once that mount is done. If we then however try to walk into that directory, the automount fails for the similar reasons: # cd /mnt/test1/scratch/ -bash: cd: /mnt/test1/scratch: Device or resource busy The story I've gotten from the SELinux folks that I've talked to is that this is desirable behavior. In SELinux-land, mounting the same data under different contexts is wrong -- there can be only one. Cc: Steve Dickson Cc: Stephen Smalley Signed-off-by: Jeff Layton Acked-by: Eric Paris Signed-off-by: James Morris diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 95cdcb2..6b4bf76 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2380,10 +2380,9 @@ int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot, struct nfs_mount_info *mount_info) { /* clone any lsm security options from the parent to the new sb */ - security_sb_clone_mnt_opts(mount_info->cloned->sb, s); if (mntroot->d_inode->i_op != NFS_SB(s)->nfs_client->rpc_ops->dir_inode_ops) return -ESTALE; - return 0; + return security_sb_clone_mnt_opts(mount_info->cloned->sb, s); } EXPORT_SYMBOL_GPL(nfs_clone_sb_security); diff --git a/include/linux/security.h b/include/linux/security.h index eee7478..4c7058d 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1436,7 +1436,7 @@ struct security_operations { struct path *new_path); int (*sb_set_mnt_opts) (struct super_block *sb, struct security_mnt_opts *opts); - void (*sb_clone_mnt_opts) (const struct super_block *oldsb, + int (*sb_clone_mnt_opts) (const struct super_block *oldsb, struct super_block *newsb); int (*sb_parse_opts_str) (char *options, struct security_mnt_opts *opts); @@ -1721,7 +1721,7 @@ int security_sb_mount(const char *dev_name, struct path *path, int security_sb_umount(struct vfsmount *mnt, int flags); int security_sb_pivotroot(struct path *old_path, struct path *new_path); int security_sb_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *opts); -void security_sb_clone_mnt_opts(const struct super_block *oldsb, +int security_sb_clone_mnt_opts(const struct super_block *oldsb, struct super_block *newsb); int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts); @@ -2011,9 +2011,11 @@ static inline int security_sb_set_mnt_opts(struct super_block *sb, return 0; } -static inline void security_sb_clone_mnt_opts(const struct super_block *oldsb, +static inline int security_sb_clone_mnt_opts(const struct super_block *oldsb, struct super_block *newsb) -{ } +{ + return 0; +} static inline int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts) { diff --git a/security/capability.c b/security/capability.c index 5797750..a6290b6 100644 --- a/security/capability.c +++ b/security/capability.c @@ -98,9 +98,10 @@ static int cap_sb_set_mnt_opts(struct super_block *sb, return 0; } -static void cap_sb_clone_mnt_opts(const struct super_block *oldsb, +static int cap_sb_clone_mnt_opts(const struct super_block *oldsb, struct super_block *newsb) { + return 0; } static int cap_sb_parse_opts_str(char *options, struct security_mnt_opts *opts) diff --git a/security/security.c b/security/security.c index 7b88c6a..108281d 100644 --- a/security/security.c +++ b/security/security.c @@ -299,10 +299,10 @@ int security_sb_set_mnt_opts(struct super_block *sb, } EXPORT_SYMBOL(security_sb_set_mnt_opts); -void security_sb_clone_mnt_opts(const struct super_block *oldsb, +int security_sb_clone_mnt_opts(const struct super_block *oldsb, struct super_block *newsb) { - security_ops->sb_clone_mnt_opts(oldsb, newsb); + return security_ops->sb_clone_mnt_opts(oldsb, newsb); } EXPORT_SYMBOL(security_sb_clone_mnt_opts); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 2fa28c8..3c02be3 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -750,7 +750,37 @@ out_double_mount: goto out; } -static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb, +static int selinux_cmp_sb_context(const struct super_block *oldsb, + const struct super_block *newsb) +{ + struct superblock_security_struct *old = oldsb->s_security; + struct superblock_security_struct *new = newsb->s_security; + char oldflags = old->flags & SE_MNTMASK; + char newflags = new->flags & SE_MNTMASK; + + if (oldflags != newflags) + goto mismatch; + if ((oldflags & FSCONTEXT_MNT) && old->sid != new->sid) + goto mismatch; + if ((oldflags & CONTEXT_MNT) && old->mntpoint_sid != new->mntpoint_sid) + goto mismatch; + if ((oldflags & DEFCONTEXT_MNT) && old->def_sid != new->def_sid) + goto mismatch; + if (oldflags & ROOTCONTEXT_MNT) { + struct inode_security_struct *oldroot = oldsb->s_root->d_inode->i_security; + struct inode_security_struct *newroot = newsb->s_root->d_inode->i_security; + if (oldroot->sid != newroot->sid) + goto mismatch; + } + return 0; +mismatch: + printk(KERN_WARNING "SELinux: mount invalid. Same superblock, " + "different security settings for (dev %s, " + "type %s)\n", newsb->s_id, newsb->s_type->name); + return -EBUSY; +} + +static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, struct super_block *newsb) { const struct superblock_security_struct *oldsbsec = oldsb->s_security; @@ -765,14 +795,14 @@ static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb, * mount options. thus we can safely deal with this superblock later */ if (!ss_initialized) - return; + return 0; /* how can we clone if the old one wasn't set up?? */ BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED)); - /* if fs is reusing a sb, just let its options stand... */ + /* if fs is reusing a sb, make sure that the contexts match */ if (newsbsec->flags & SE_SBINITIALIZED) - return; + return selinux_cmp_sb_context(oldsb, newsb); mutex_lock(&newsbsec->lock); @@ -805,6 +835,7 @@ static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb, sb_finish_set_opts(newsb); mutex_unlock(&newsbsec->lock); + return 0; } static int selinux_parse_opts_str(char *options, -- cgit v0.10.2 From 958d2c2f4ad905e3ffa1711d19184d21d9b00cc1 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Tue, 2 Apr 2013 11:41:18 -0700 Subject: Smack: include magic.h in smackfs.c As reported for linux-next: Tree for Apr 2 (smack) Add the required include for smackfs.c Signed-off-by: Casey Schaufler Reported-by: Randy Dunlap Acked-by: Randy Dunlap Signed-off-by: James Morris diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 2479a41..53a08b8 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "smack.h" /* -- cgit v0.10.2 From 2d089f82aeb59fd58f33f2bbf4288e4049426e17 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Thu, 28 Feb 2013 16:15:48 -0600 Subject: tpm/tpm_i2c_stm_st33: formatting and white space changes Signed-off-by: Peter Huewe Signed-off-by: Kent Yoder diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c index 1f5f71e..743ffec 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c @@ -178,7 +178,7 @@ static long _wait_for_interrupt_serirq_timeout(struct tpm_chip *chip, struct i2c_client *client; struct st33zp24_platform_data *pin_infos; - client = (struct i2c_client *) TPM_VPRIV(chip); + client = (struct i2c_client *)TPM_VPRIV(chip); pin_infos = client->dev.platform_data; status = wait_for_completion_interruptible_timeout( @@ -197,12 +197,12 @@ static int wait_for_serirq_timeout(struct tpm_chip *chip, bool condition, int status = 2; struct i2c_client *client; - client = (struct i2c_client *) TPM_VPRIV(chip); + client = (struct i2c_client *)TPM_VPRIV(chip); status = _wait_for_interrupt_serirq_timeout(chip, timeout); if (!status) { status = -EBUSY; - } else{ + } else { clear_interruption(client); if (condition) status = 1; @@ -219,7 +219,7 @@ static void tpm_stm_i2c_cancel(struct tpm_chip *chip) struct i2c_client *client; u8 data; - client = (struct i2c_client *) TPM_VPRIV(chip); + client = (struct i2c_client *)TPM_VPRIV(chip); data = TPM_STS_COMMAND_READY; I2C_WRITE_DATA(client, TPM_STS, &data, 1); @@ -236,7 +236,7 @@ static u8 tpm_stm_i2c_status(struct tpm_chip *chip) { struct i2c_client *client; u8 data; - client = (struct i2c_client *) TPM_VPRIV(chip); + client = (struct i2c_client *)TPM_VPRIV(chip); I2C_READ_DATA(client, TPM_STS, &data, 1); return data; @@ -254,7 +254,7 @@ static int check_locality(struct tpm_chip *chip) u8 data; u8 status; - client = (struct i2c_client *) TPM_VPRIV(chip); + client = (struct i2c_client *)TPM_VPRIV(chip); status = I2C_READ_DATA(client, TPM_ACCESS, &data, 1); if (status && (data & @@ -278,7 +278,7 @@ static int request_locality(struct tpm_chip *chip) struct i2c_client *client; u8 data; - client = (struct i2c_client *) TPM_VPRIV(chip); + client = (struct i2c_client *)TPM_VPRIV(chip); if (check_locality(chip) == chip->vendor.locality) return chip->vendor.locality; @@ -294,7 +294,7 @@ static int request_locality(struct tpm_chip *chip) chip->vendor.timeout_a); if (rc > 0) return chip->vendor.locality; - } else{ + } else { stop = jiffies + chip->vendor.timeout_a; do { if (check_locality(chip) >= 0) @@ -316,7 +316,7 @@ static void release_locality(struct tpm_chip *chip) struct i2c_client *client; u8 data; - client = (struct i2c_client *) TPM_VPRIV(chip); + client = (struct i2c_client *)TPM_VPRIV(chip); data = TPM_ACCESS_ACTIVE_LOCALITY; I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1); @@ -333,7 +333,7 @@ static int get_burstcount(struct tpm_chip *chip) int burstcnt, status; u8 tpm_reg, temp; - struct i2c_client *client = (struct i2c_client *) TPM_VPRIV(chip); + struct i2c_client *client = (struct i2c_client *)TPM_VPRIV(chip); stop = jiffies + chip->vendor.timeout_d; do { @@ -379,7 +379,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, mask), timeout); if (rc > 0) return 0; - } else{ + } else { stop = jiffies + timeout; do { msleep(TPM_TIMEOUT); @@ -403,7 +403,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) int size = 0, burstcnt, len; struct i2c_client *client; - client = (struct i2c_client *) TPM_VPRIV(chip); + client = (struct i2c_client *)TPM_VPRIV(chip); while (size < count && wait_for_stat(chip, @@ -433,7 +433,7 @@ static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) disable_irq_nosync(irq); - client = (struct i2c_client *) TPM_VPRIV(chip); + client = (struct i2c_client *)TPM_VPRIV(chip); pin_infos = client->dev.platform_data; complete(&pin_infos->irq_detection); @@ -453,8 +453,7 @@ static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, size_t len) { - u32 status, - burstcnt = 0, i, size; + u32 status, burstcnt = 0, i, size; int ret; u8 data; struct i2c_client *client; @@ -483,7 +482,7 @@ static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, } } - for (i = 0 ; i < len - 1 ;) { + for (i = 0; i < len - 1;) { burstcnt = get_burstcount(chip); size = min_t(int, len - i - 1, burstcnt); ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf, size); @@ -547,7 +546,7 @@ static int tpm_stm_i2c_recv(struct tpm_chip *chip, unsigned char *buf, goto out; } - expected = be32_to_cpu(*(__be32 *) (buf + 2)); + expected = be32_to_cpu(*(__be32 *)(buf + 2)); if (expected > count) { size = -EIO; goto out; @@ -569,7 +568,7 @@ out: static bool tpm_st33_i2c_req_canceled(struct tpm_chip *chip, u8 status) { - return (status == TPM_STS_COMMAND_READY); + return (status == TPM_STS_COMMAND_READY); } static const struct file_operations tpm_st33_i2c_fops = { @@ -617,7 +616,7 @@ static struct tpm_vendor_specific st_i2c_tpm = { .miscdev = {.fops = &tpm_st33_i2c_fops,}, }; -static int interrupts ; +static int interrupts; module_param(interrupts, int, 0444); MODULE_PARM_DESC(interrupts, "Enable interrupts"); @@ -714,7 +713,7 @@ tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) "TPM SERIRQ management", chip); if (err < 0) { dev_err(chip->dev , "TPM SERIRQ signals %d not available\n", - gpio_to_irq(platform_data->io_serirq)); + gpio_to_irq(platform_data->io_serirq)); goto _irq_set; } @@ -754,7 +753,7 @@ tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_info(chip->dev, "TPM I2C Initialized\n"); return 0; _irq_set: - free_irq(gpio_to_irq(platform_data->io_serirq), (void *) chip); + free_irq(gpio_to_irq(platform_data->io_serirq), (void *)chip); _gpio_init2: if (interrupts) gpio_free(platform_data->io_serirq); @@ -784,7 +783,7 @@ static int tpm_st33_i2c_remove(struct i2c_client *client) { struct tpm_chip *chip = (struct tpm_chip *)i2c_get_clientdata(client); struct st33zp24_platform_data *pin_infos = - ((struct i2c_client *) TPM_VPRIV(chip))->dev.platform_data; + ((struct i2c_client *)TPM_VPRIV(chip))->dev.platform_data; if (pin_infos != NULL) { free_irq(pin_infos->io_serirq, chip); @@ -823,9 +822,9 @@ static int tpm_st33_i2c_pm_suspend(struct device *dev) struct st33zp24_platform_data *pin_infos = dev->platform_data; int ret = 0; - if (power_mgt) + if (power_mgt) { gpio_set_value(pin_infos->io_lpcpd, 0); - else{ + } else { if (chip->data_buffer == NULL) chip->data_buffer = pin_infos->tpm_i2c_buffer[0]; ret = tpm_pm_suspend(dev); @@ -851,12 +850,12 @@ static int tpm_st33_i2c_pm_resume(struct device *dev) (chip->vendor.status(chip) & TPM_STS_VALID) == TPM_STS_VALID, chip->vendor.timeout_b); - } else{ - if (chip->data_buffer == NULL) - chip->data_buffer = pin_infos->tpm_i2c_buffer[0]; - ret = tpm_pm_resume(dev); - if (!ret) - tpm_do_selftest(chip); + } else { + if (chip->data_buffer == NULL) + chip->data_buffer = pin_infos->tpm_i2c_buffer[0]; + ret = tpm_pm_resume(dev); + if (!ret) + tpm_do_selftest(chip); } return ret; } /* tpm_st33_i2c_pm_resume() */ @@ -867,7 +866,8 @@ static const struct i2c_device_id tpm_st33_i2c_id[] = { {} }; MODULE_DEVICE_TABLE(i2c, tpm_st33_i2c_id); -static SIMPLE_DEV_PM_OPS(tpm_st33_i2c_ops, tpm_st33_i2c_pm_suspend, tpm_st33_i2c_pm_resume); +static SIMPLE_DEV_PM_OPS(tpm_st33_i2c_ops, tpm_st33_i2c_pm_suspend, + tpm_st33_i2c_pm_resume); static struct i2c_driver tpm_st33_i2c_driver = { .driver = { .owner = THIS_MODULE, -- cgit v0.10.2 From e361200bfc3b72de8db246de209da55d6d60fd34 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Thu, 28 Feb 2013 16:24:50 -0600 Subject: drivers/char/tpm/tpm_ppi: use strlcpy instead of strncpy Ensure that the 'version' string includes a NULL terminator after its copied out of the acpi table. Signed-off-by: Chen Gang Signed-off-by: Kent Yoder diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index 720ebcf..2168d15 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -158,9 +158,9 @@ static ssize_t tpm_store_ppi_request(struct device *dev, ACPI_TYPE_STRING); if (ACPI_FAILURE(status)) return -ENOMEM; - strncpy(version, + strlcpy(version, ((union acpi_object *)output.pointer)->string.pointer, - PPI_VERSION_LEN); + PPI_VERSION_LEN + 1); kfree(output.pointer); output.length = ACPI_ALLOCATE_BUFFER; output.pointer = NULL; @@ -237,9 +237,9 @@ static ssize_t tpm_show_ppi_transition_action(struct device *dev, ACPI_TYPE_STRING); if (ACPI_FAILURE(status)) return -ENOMEM; - strncpy(version, + strlcpy(version, ((union acpi_object *)output.pointer)->string.pointer, - PPI_VERSION_LEN); + PPI_VERSION_LEN + 1); /* * PPI spec defines params[3].type as empty package, but some platforms * (e.g. Capella with PPI 1.0) need integer/string/buffer type, so for @@ -351,7 +351,7 @@ cleanup: static ssize_t show_ppi_operations(char *buf, u32 start, u32 end) { char *str = buf; - char version[PPI_VERSION_LEN]; + char version[PPI_VERSION_LEN + 1]; acpi_handle handle; acpi_status status; struct acpi_object_list input; @@ -381,9 +381,9 @@ static ssize_t show_ppi_operations(char *buf, u32 start, u32 end) if (ACPI_FAILURE(status)) return -ENOMEM; - strncpy(version, + strlcpy(version, ((union acpi_object *)output.pointer)->string.pointer, - PPI_VERSION_LEN); + PPI_VERSION_LEN + 1); kfree(output.pointer); output.length = ACPI_ALLOCATE_BUFFER; output.pointer = NULL; -- cgit v0.10.2 From eef8b6291987c059dacb07dd516d4ada0e3362af Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Thu, 28 Feb 2013 11:06:11 +0100 Subject: char/tpm: Convert struct i2c_msg initialization to C99 format Convert the struct i2c_msg initialization to C99 format. This makes maintaining and editing the code simpler. Also helps once other fields like transferred are added in future. Thanks to Julia Lawall for automating the conversion. Signed-off-by: Shubhrajyoti D Acked-by: Peter Huewe Signed-off-by: Jean Delvare Signed-off-by: Kent Yoder diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 8fe7ac3..8e47e2b 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -90,8 +90,17 @@ static struct i2c_driver tpm_tis_i2c_driver; static int iic_tpm_read(u8 addr, u8 *buffer, size_t len) { - struct i2c_msg msg1 = { tpm_dev.client->addr, 0, 1, &addr }; - struct i2c_msg msg2 = { tpm_dev.client->addr, I2C_M_RD, len, buffer }; + struct i2c_msg msg1 = { + .addr = tpm_dev.client->addr, + .len = 1, + .buf = &addr + }; + struct i2c_msg msg2 = { + .addr = tpm_dev.client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buffer + }; int rc; int count; @@ -138,7 +147,11 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, int rc = -EIO; int count; - struct i2c_msg msg1 = { tpm_dev.client->addr, 0, len + 1, tpm_dev.buf }; + struct i2c_msg msg1 = { + .addr = tpm_dev.client->addr, + .len = len + 1, + .buf = tpm_dev.buf + }; if (len > TPM_BUFSIZE) return -EINVAL; -- cgit v0.10.2 From c61c86dd6e0a8037be73cf27212f389e46af60a4 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 4 Mar 2013 15:41:46 +0100 Subject: tpm: Add support for new Infineon I2C TPM (SLB 9645 TT 1.2 I2C) This driver adds support for Infineon's new SLB 9645 TT 1.2 I2C TPMs, which supports clockstretching, combined reads and a bus speed of up to 400khz. The device also has a new device id. The driver works now also fine with device trees, so you can instantiate your device by adding: + tpm { + compatible = "infineon,slb9645tt"; + reg = <0x20>; + }; for SLB 9645 devices or + tpm { + compatible = "infineon,slb9635tt"; + reg = <0x20>; + }; for SLB 9635 devices to your device tree. tpm_i2c_infineon is also retained as a compatible id as a fallback to slb9635 protocol. The driver was tested on Beaglebone. Signed-off-by: Peter Huewe Signed-off-by: Kent Yoder diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt index 446859f..ad6a738 100644 --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt @@ -35,6 +35,8 @@ fsl,mc13892 MC13892: Power Management Integrated Circuit (PMIC) for i.MX35/51 fsl,mma8450 MMA8450Q: Xtrinsic Low-power, 3-axis Xtrinsic Accelerometer fsl,mpr121 MPR121: Proximity Capacitive Touch Sensor Controller fsl,sgtl5000 SGTL5000: Ultra Low-Power Audio Codec +infineon,slb9635tt Infineon SLB9635 (Soft-) I2C TPM (old protocol, max 100khz) +infineon,slb9645tt Infineon SLB9645 I2C TPM (new protocol, max 400khz) maxim,ds1050 5 Bit Programmable, Pulse-Width Modulator maxim,max1237 Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs maxim,max6625 9-Bit/12-Bit Temperature Sensors with I²C-Compatible Serial Interface diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 8e47e2b..bd9a295 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Infineon Technologies + * Copyright (C) 2012,2013 Infineon Technologies * * Authors: * Peter Huewe @@ -56,13 +56,21 @@ #define TPM_TIMEOUT_US_HI (TPM_TIMEOUT_US_LOW + 2000) /* expected value for DIDVID register */ -#define TPM_TIS_I2C_DID_VID 0x000b15d1L +#define TPM_TIS_I2C_DID_VID_9635 0xd1150b00L +#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L + +enum i2c_chip_type { + SLB9635, + SLB9645, + UNKNOWN, +}; /* Structure to store I2C TPM specific stuff */ struct tpm_inf_dev { struct i2c_client *client; u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */ struct tpm_chip *chip; + enum i2c_chip_type chip_type; }; static struct tpm_inf_dev tpm_dev; @@ -101,8 +109,9 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len) .len = len, .buf = buffer }; + struct i2c_msg msgs[] = {msg1, msg2}; - int rc; + int rc = 0; int count; /* Lock the adapter for the duration of the whole sequence. */ @@ -110,30 +119,49 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len) return -EOPNOTSUPP; i2c_lock_adapter(tpm_dev.client->adapter); - for (count = 0; count < MAX_COUNT; count++) { - rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1); - if (rc > 0) - break; /* break here to skip sleep */ - - usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); - } - - if (rc <= 0) - goto out; - - /* After the TPM has successfully received the register address it needs - * some time, thus we're sleeping here again, before retrieving the data - */ - for (count = 0; count < MAX_COUNT; count++) { - usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); - rc = __i2c_transfer(tpm_dev.client->adapter, &msg2, 1); - if (rc > 0) - break; + if (tpm_dev.chip_type == SLB9645) { + /* use a combined read for newer chips + * unfortunately the smbus functions are not suitable due to + * the 32 byte limit of the smbus. + * retries should usually not be needed, but are kept just to + * be on the safe side. + */ + for (count = 0; count < MAX_COUNT; count++) { + rc = __i2c_transfer(tpm_dev.client->adapter, msgs, 2); + if (rc > 0) + break; /* break here to skip sleep */ + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); + } + } else { + /* slb9635 protocol should work in all cases */ + for (count = 0; count < MAX_COUNT; count++) { + rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1); + if (rc > 0) + break; /* break here to skip sleep */ + + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); + } + if (rc <= 0) + goto out; + + /* After the TPM has successfully received the register address + * it needs some time, thus we're sleeping here again, before + * retrieving the data + */ + for (count = 0; count < MAX_COUNT; count++) { + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); + rc = __i2c_transfer(tpm_dev.client->adapter, &msg2, 1); + if (rc > 0) + break; + } } out: i2c_unlock_adapter(tpm_dev.client->adapter); + /* take care of 'guard time' */ + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); + if (rc <= 0) return -EIO; @@ -167,16 +195,19 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, /* * NOTE: We have to use these special mechanisms here and unfortunately * cannot rely on the standard behavior of i2c_transfer. + * Even for newer chips the smbus functions are not + * suitable due to the 32 byte limit of the smbus. */ for (count = 0; count < max_count; count++) { rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1); if (rc > 0) break; - usleep_range(sleep_low, sleep_hi); } i2c_unlock_adapter(tpm_dev.client->adapter); + /* take care of 'guard time' */ + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); if (rc <= 0) return -EIO; @@ -296,11 +327,18 @@ static int request_locality(struct tpm_chip *chip, int loc) static u8 tpm_tis_i2c_status(struct tpm_chip *chip) { /* NOTE: since I2C read may fail, return 0 in this case --> time-out */ - u8 buf; - if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0) - return 0; - else - return buf; + u8 buf = 0xFF; + u8 i = 0; + + do { + if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0) + return 0; + + i++; + /* if locallity is set STS should not be 0xFF */ + } while ((buf == 0xFF) && i < 10); + + return buf; } static void tpm_tis_i2c_ready(struct tpm_chip *chip) @@ -341,7 +379,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, /* check current status */ *status = tpm_tis_i2c_status(chip); - if ((*status & mask) == mask) + if ((*status != 0xFF) && (*status & mask) == mask) return 0; stop = jiffies + timeout; @@ -385,7 +423,6 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) /* avoid endless loop in case of broken HW */ if (retries > MAX_COUNT_LONG) return -EIO; - } return size; } @@ -493,7 +530,6 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) rc = -EIO; goto out_err; } - } /* write last byte */ @@ -581,6 +617,7 @@ static int tpm_tis_i2c_init(struct device *dev) chip = tpm_register_hardware(dev, &tpm_tis_i2c); if (!chip) { + dev_err(dev, "could not register hardware\n"); rc = -ENODEV; goto out_err; } @@ -595,20 +632,24 @@ static int tpm_tis_i2c_init(struct device *dev) chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); if (request_locality(chip, 0) != 0) { + dev_err(dev, "could not request locality\n"); rc = -ENODEV; goto out_vendor; } /* read four bytes from DID_VID register */ if (iic_tpm_read(TPM_DID_VID(0), (u8 *)&vendor, 4) < 0) { + dev_err(dev, "could not read vendor id\n"); rc = -EIO; goto out_release; } - /* create DID_VID register value, after swapping to little-endian */ - vendor = be32_to_cpu((__be32) vendor); - - if (vendor != TPM_TIS_I2C_DID_VID) { + if (vendor == TPM_TIS_I2C_DID_VID_9645) { + tpm_dev.chip_type = SLB9645; + } else if (vendor == TPM_TIS_I2C_DID_VID_9635) { + tpm_dev.chip_type = SLB9635; + } else { + dev_err(dev, "vendor id did not match! ID was %08x\n", vendor); rc = -ENODEV; goto out_release; } @@ -644,22 +685,38 @@ out_err: static const struct i2c_device_id tpm_tis_i2c_table[] = { {"tpm_i2c_infineon", 0}, + {"slb9635tt", 0}, + {"slb9645tt", 1}, {}, }; MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_table); + +#ifdef CONFIG_OF +static const struct of_device_id tpm_tis_i2c_of_match[] = { + { .compatible = "infineon,tpm_i2c_infineon", .data = (void *)0 }, + { .compatible = "infineon,slb9635tt", .data = (void *)0 }, + { .compatible = "infineon,slb9645tt", .data = (void *)1 }, + {}, +}; +MODULE_DEVICE_TABLE(of, tpm_tis_i2c_of_match); +#endif + static SIMPLE_DEV_PM_OPS(tpm_tis_i2c_ops, tpm_pm_suspend, tpm_pm_resume); static int tpm_tis_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int rc; - if (tpm_dev.client != NULL) + struct device *dev = &(client->dev); + + if (tpm_dev.client != NULL) { + dev_err(dev, "This driver only supports one client at a time\n"); return -EBUSY; /* We only support one client */ + } if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_err(&client->dev, - "no algorithms associated to the i2c bus\n"); + dev_err(dev, "no algorithms associated to the i2c bus\n"); return -ENODEV; } @@ -695,7 +752,6 @@ static int tpm_tis_i2c_remove(struct i2c_client *client) } static struct i2c_driver tpm_tis_i2c_driver = { - .id_table = tpm_tis_i2c_table, .probe = tpm_tis_i2c_probe, .remove = tpm_tis_i2c_remove, @@ -703,11 +759,12 @@ static struct i2c_driver tpm_tis_i2c_driver = { .name = "tpm_i2c_infineon", .owner = THIS_MODULE, .pm = &tpm_tis_i2c_ops, + .of_match_table = of_match_ptr(tpm_tis_i2c_of_match), }, }; module_i2c_driver(tpm_tis_i2c_driver); MODULE_AUTHOR("Peter Huewe "); MODULE_DESCRIPTION("TPM TIS I2C Infineon Driver"); -MODULE_VERSION("2.1.5"); +MODULE_VERSION("2.2.0"); MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 3320280b6fd7df65e3f211c0e4db66f637ca035f Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 6 Mar 2013 14:27:54 +0530 Subject: tpm_i2c_stm_st33: Remove duplicate inclusion of header files module.h and sched.h were included twice. Signed-off-by: Sachin Kamat Signed-off-by: Kent Yoder diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c index 743ffec..5bb8e2d 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -50,7 +49,6 @@ #include #include #include -#include #include "tpm.h" #include "tpm_i2c_stm_st33.h" -- cgit v0.10.2 From 21dc02eab989d260fd9e22b72a29ac139a727cdd Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 4 Mar 2013 22:08:54 +0100 Subject: tpm/tpm_i2c_infineon.c: Add OF attributes type and name to the of_device_id table entries As the subject says. It's probably a good idea to have these fields populated. Signed-off-by: Peter Huewe Signed-off-by: Kent Yoder diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index bd9a295..d83bb8c 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -694,9 +694,24 @@ MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_table); #ifdef CONFIG_OF static const struct of_device_id tpm_tis_i2c_of_match[] = { - { .compatible = "infineon,tpm_i2c_infineon", .data = (void *)0 }, - { .compatible = "infineon,slb9635tt", .data = (void *)0 }, - { .compatible = "infineon,slb9645tt", .data = (void *)1 }, + { + .name = "tpm_i2c_infineon", + .type = "tpm", + .compatible = "infineon,tpm_i2c_infineon", + .data = (void *)0 + }, + { + .name = "slb9635tt", + .type = "tpm", + .compatible = "infineon,slb9635tt", + .data = (void *)0 + }, + { + .name = "slb9645tt", + .type = "tpm", + .compatible = "infineon,slb9645tt", + .data = (void *)1 + }, {}, }; MODULE_DEVICE_TABLE(of, tpm_tis_i2c_of_match); -- cgit v0.10.2 From 6aa4ef4dab92fc8d4f0e5ea735ae3fd520af510b Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 4 Mar 2013 22:17:15 +0100 Subject: tpm/tpm_i2c_infineon: Add small comment about return value of __i2c_transfer Kent Yoder indicated that the code might be a bit clearer with a comment here, so this patch adds a small explanation of the code. Signed-off-by: Peter Huewe Signed-off-by: Kent Yoder diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index d83bb8c..37d5dcc 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -162,6 +162,10 @@ out: /* take care of 'guard time' */ usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); + /* __i2c_transfer returns the number of successfully transferred + * messages. + * So rc should be greater than 0 here otherwise we have an error. + */ if (rc <= 0) return -EIO; @@ -208,6 +212,11 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, i2c_unlock_adapter(tpm_dev.client->adapter); /* take care of 'guard time' */ usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); + + /* __i2c_transfer returns the number of successfully transferred + * messages. + * So rc should be greater than 0 here otherwise we have an error. + */ if (rc <= 0) return -EIO; -- cgit v0.10.2 From 32d33b29ba077d6b45de35f2181e0a7411b162f4 Mon Sep 17 00:00:00 2001 From: Duncan Laurie Date: Sun, 17 Mar 2013 14:56:39 -0700 Subject: TPM: Retry SaveState command in suspend path If the TPM has already been sent a SaveState command before the driver is loaded it may have problems sending that same command again later. This issue is seen with the Chromebook Pixel due to a firmware bug in the legacy mode boot path which is sending the SaveState command before booting the kernel. More information is available at http://crbug.com/203524 This change introduces a retry of the SaveState command in the suspend path in order to work around this issue. A future firmware update should fix this but this is also a trivial workaround in the driver that has no effect on systems that do not show this problem. When this does happen the TPM responds with a non-fatal TPM_RETRY code that is defined in the specification: The TPM is too busy to respond to the command immediately, but the command could be resubmitted at a later time. The TPM MAY return TPM_RETRY for any command at any time. It can take several seconds before the TPM will respond again. I measured a typical time between 3 and 4 seconds and the timeout is set at a safe 5 seconds. It is also possible to reproduce this with commands via /dev/tpm0. The bug linked above has a python script attached which can be used to test for this problem. I tested a variety of TPMs from Infineon, Nuvoton, Atmel, and STMicro but was only able to reproduce this with LPC and I2C TPMs from Infineon. The TPM specification only loosely defines this behavior: TPM Main Level 2 Part 3 v1.2 r116, section 3.3. TPM_SaveState: The TPM MAY declare all preserved values invalid in response to any command other than TPM_Init. TCG PC Client BIOS Spec 1.21 section 8.3.1. After issuing a TPM_SaveState command, the OS SHOULD NOT issue TPM commands before transitioning to S3 without issuing another TPM_SaveState command. TCG PC Client TIS 1.21, section 4. Power Management: The TPM_SaveState command allows a Static OS to indicate to the TPM that the platform may enter a low power state where the TPM will be required to enter into the D3 power state. The use of the term "may" is significant in that there is no requirement for the platform to actually enter the low power state after sending the TPM_SaveState command. The software may, in fact, send subsequent commands after sending the TPM_SaveState command. Change-Id: I52b41e826412688e5b6c8ddd3bb16409939704e9 Signed-off-by: Duncan Laurie Signed-off-by: Kent Yoder diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 0d2e82f..7c3b3dc 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1337,7 +1337,7 @@ int tpm_pm_suspend(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); struct tpm_cmd_t cmd; - int rc; + int rc, try; u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 }; @@ -1355,9 +1355,32 @@ int tpm_pm_suspend(struct device *dev) } /* now do the actual savestate */ - cmd.header.in = savestate_header; - rc = transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, - "sending savestate before suspend"); + for (try = 0; try < TPM_RETRY; try++) { + cmd.header.in = savestate_header; + rc = transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, NULL); + + /* + * If the TPM indicates that it is too busy to respond to + * this command then retry before giving up. It can take + * several seconds for this TPM to be ready. + * + * This can happen if the TPM has already been sent the + * SaveState command before the driver has loaded. TCG 1.2 + * specification states that any communication after SaveState + * may cause the TPM to invalidate previously saved state. + */ + if (rc != TPM_WARN_RETRY) + break; + msleep(TPM_TIMEOUT_RETRY); + } + + if (rc) + dev_err(chip->dev, + "Error (%d) sending savestate before suspend\n", rc); + else if (try > 0) + dev_warn(chip->dev, "TPM savestate took %dms\n", + try * TPM_TIMEOUT_RETRY); + return rc; } EXPORT_SYMBOL_GPL(tpm_pm_suspend); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 81b5201..0770d1d 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -32,10 +32,12 @@ enum tpm_const { TPM_MINOR = 224, /* officially assigned */ TPM_BUFSIZE = 4096, TPM_NUM_DEVICES = 256, + TPM_RETRY = 50, /* 5 seconds */ }; enum tpm_timeout { TPM_TIMEOUT = 5, /* msecs */ + TPM_TIMEOUT_RETRY = 100 /* msecs */ }; /* TPM addresses */ @@ -44,6 +46,7 @@ enum tpm_addr { TPM_ADDR = 0x4E, }; +#define TPM_WARN_RETRY 0x800 #define TPM_WARN_DOING_SELFTEST 0x802 #define TPM_ERR_DEACTIVATED 0x6 #define TPM_ERR_DISABLED 0x7 -- cgit v0.10.2 From df2c2afba4fa867616436364633c7612ccfba84b Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 15 Apr 2013 11:27:20 -0400 Subject: ima: eliminate passing d_name.name to process_measurement() Passing a pointer to the dentry name, as a parameter to process_measurement(), causes a race condition with rename() and is unnecessary, as the dentry name is already accessible via the file parameter. In the normal case, we use the full pathname as provided by brpm->filename, bprm->interp, or ima_d_path(). Only on ima_d_path() failure, do we fallback to using the d_name.name, which points either to external memory or d_iname. Reported-by: Al Viro Signed-off-by: Mimi Zohar Signed-off-by: James Morris diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 3b3b7e6..6c491a6 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -189,11 +189,9 @@ static int process_measurement(struct file *file, const char *filename, if (rc != 0) goto out_digsig; - if (function != BPRM_CHECK) - pathname = ima_d_path(&file->f_path, &pathbuf); - + pathname = !filename ? ima_d_path(&file->f_path, &pathbuf) : filename; if (!pathname) - pathname = filename; + pathname = (const char *)file->f_dentry->d_name.name; if (action & IMA_MEASURE) ima_store_measurement(iint, file, pathname); @@ -226,8 +224,7 @@ out: int ima_file_mmap(struct file *file, unsigned long prot) { if (file && (prot & PROT_EXEC)) - return process_measurement(file, file->f_dentry->d_name.name, - MAY_EXEC, MMAP_CHECK); + return process_measurement(file, NULL, MAY_EXEC, MMAP_CHECK); return 0; } @@ -265,7 +262,7 @@ int ima_bprm_check(struct linux_binprm *bprm) int ima_file_check(struct file *file, int mask) { ima_rdwr_violation_check(file); - return process_measurement(file, file->f_dentry->d_name.name, + return process_measurement(file, NULL, mask & (MAY_READ | MAY_WRITE | MAY_EXEC), FILE_CHECK); } @@ -290,8 +287,7 @@ int ima_module_check(struct file *file) #endif return 0; /* We rely on module signature checking */ } - return process_measurement(file, file->f_dentry->d_name.name, - MAY_EXEC, MODULE_CHECK); + return process_measurement(file, NULL, MAY_EXEC, MODULE_CHECK); } static int __init init_ima(void) -- cgit v0.10.2