From fd5c9d230d2ac8a2594dfd15f0cca678fd7a64c7 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Thu, 7 Aug 2014 20:52:33 +0400 Subject: Smack: fix behavior of smack_inode_listsecurity Security operation ->inode_listsecurity is used for generating list of available extended attributes for syscall listxattr. Currently it's used only in nfs4 or if filesystem doesn't provide i_op->listxattr. The list is the set of NULL-terminated names, one after the other. This method must include zero byte at the and into result. Also this function must return length even if string does not fit into output buffer or it is NULL, see similar method in selinux and man listxattr. Signed-off-by: Konstantin Khlebnikov --- security/smack/smack_lsm.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index e6ab307ce86e..b11ab23b328b 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1122,13 +1122,12 @@ static int smack_inode_getsecurity(const struct inode *inode, static int smack_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size) { - int len = strlen(XATTR_NAME_SMACK); + int len = sizeof(XATTR_NAME_SMACK); - if (buffer != NULL && len <= buffer_size) { + if (buffer != NULL && len <= buffer_size) memcpy(buffer, XATTR_NAME_SMACK, len); - return len; - } - return -EINVAL; + + return len; } /** -- cgit v1.2.3 From b862e561bad6372872f5bf98d95f4131d265b110 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Thu, 7 Aug 2014 20:52:43 +0400 Subject: Smack: handle zero-length security labels without panic Zero-length security labels are invalid but kernel should handle them. This patch fixes kernel panic after setting zero-length security labels: # attr -S -s "SMACK64" -V "" file And after writing zero-length string into smackfs files syslog and onlycp: # python -c 'import os; os.write(1, "")' > /smack/syslog The problem is caused by brain-damaged logic in function smk_parse_smack() which takes pointer to buffer and its length but if length below or equal zero it thinks that the buffer is zero-terminated. Unfortunately callers of this function are widely used and proper fix requires serious refactoring. Signed-off-by: Konstantin Khlebnikov --- security/smack/smack_lsm.c | 2 +- security/smack/smackfs.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index b11ab23b328b..afa5ad0e7f72 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -923,7 +923,7 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, rc = -EPERM; if (rc == 0 && check_import) { - skp = smk_import_entry(value, size); + skp = size ? smk_import_entry(value, size) : NULL; if (skp == NULL || (check_star && (skp == &smack_known_star || skp == &smack_known_web))) rc = -EINVAL; diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 3c720ff10591..56a1439786a9 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -1677,7 +1677,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, if (smack_onlycap != NULL && smack_onlycap != skp) return -EPERM; - data = kzalloc(count, GFP_KERNEL); + data = kzalloc(count + 1, GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -2228,7 +2228,7 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf, if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; - data = kzalloc(count, GFP_KERNEL); + data = kzalloc(count + 1, GFP_KERNEL); if (data == NULL) return -ENOMEM; -- cgit v1.2.3 From da1b63566c469bf3e2b24182114422e16b1aa34c Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Thu, 7 Aug 2014 20:52:49 +0400 Subject: Smack: remove unneeded NULL-termination from securtity label Values of extended attributes are stored as binary blobs. NULL-termination of them isn't required. It just wastes disk space and confuses command-line tools like getfattr because they have to print that zero byte at the end. This patch removes terminating zero byte from initial security label in smack_inode_init_security and cuts it out in function smack_inode_getsecurity which is used by syscall getxattr. This change seems completely safe, because function smk_parse_smack ignores everything after first zero byte. Signed-off-by: Konstantin Khlebnikov --- security/smack/smack_lsm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index afa5ad0e7f72..16ae8534b14c 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -672,7 +672,7 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, } if (len) - *len = strlen(isp) + 1; + *len = strlen(isp); return 0; } @@ -1076,7 +1076,7 @@ static int smack_inode_getsecurity(const struct inode *inode, if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) { isp = smk_of_inode(inode); - ilen = strlen(isp) + 1; + ilen = strlen(isp); *buffer = isp; return ilen; } @@ -1101,7 +1101,7 @@ static int smack_inode_getsecurity(const struct inode *inode, else return -EOPNOTSUPP; - ilen = strlen(isp) + 1; + ilen = strlen(isp); if (rc == 0) { *buffer = isp; rc = ilen; -- cgit v1.2.3 From d83d2c26461d661384676a4eed935d925b0fcc34 Mon Sep 17 00:00:00 2001 From: Marcin Niesluchowski Date: Tue, 19 Aug 2014 14:26:32 +0200 Subject: Smack: Fix setting label on successful file open While opening with CAP_MAC_OVERRIDE file label is not set. Other calls may access it after CAP_MAC_OVERRIDE is dropped from process. Signed-off-by: Marcin Niesluchowski --- security/smack/smack_lsm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 16ae8534b14c..7091b46adab2 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1477,8 +1477,10 @@ static int smack_file_open(struct file *file, const struct cred *cred) struct smk_audit_info ad; int rc; - if (smack_privileged(CAP_MAC_OVERRIDE)) + if (smack_privileged(CAP_MAC_OVERRIDE)) { + file->f_security = isp->smk_inode; return 0; + } smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); -- cgit v1.2.3 From 7b0d0b40cd78cadb525df760ee4cac151533c2b5 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Mon, 4 Aug 2014 13:36:49 -0400 Subject: selinux: Permit bounded transitions under NO_NEW_PRIVS or NOSUID. If the callee SID is bounded by the caller SID, then allowing the transition to occur poses no risk of privilege escalation and we can therefore safely allow the transition to occur. Add this exemption for both the case where a transition was explicitly requested by the application and the case where an automatic transition is defined in policy. Signed-off-by: Stephen Smalley Reviewed-by: Andy Lutomirski Signed-off-by: Paul Moore --- security/selinux/hooks.c | 59 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b0e940497e23..6c90d491fab4 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2097,6 +2097,41 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) /* binprm security operations */ +static int check_nnp_nosuid(const struct linux_binprm *bprm, + const struct task_security_struct *old_tsec, + const struct task_security_struct *new_tsec) +{ + int nnp = (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS); + int nosuid = (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID); + int rc; + + if (!nnp && !nosuid) + return 0; /* neither NNP nor nosuid */ + + if (new_tsec->sid == old_tsec->sid) + return 0; /* No change in credentials */ + + /* + * The only transitions we permit under NNP or nosuid + * are transitions to bounded SIDs, i.e. SIDs that are + * guaranteed to only be allowed a subset of the permissions + * of the current SID. + */ + rc = security_bounded_transition(old_tsec->sid, new_tsec->sid); + if (rc) { + /* + * On failure, preserve the errno values for NNP vs nosuid. + * NNP: Operation not permitted for caller. + * nosuid: Permission denied to file. + */ + if (nnp) + return -EPERM; + else + return -EACCES; + } + return 0; +} + static int selinux_bprm_set_creds(struct linux_binprm *bprm) { const struct task_security_struct *old_tsec; @@ -2133,14 +2168,10 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) /* Reset exec SID on execve. */ new_tsec->exec_sid = 0; - /* - * Minimize confusion: if no_new_privs or nosuid and a - * transition is explicitly requested, then fail the exec. - */ - if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) - return -EPERM; - if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) - return -EACCES; + /* Fail on NNP or nosuid if not an allowed transition. */ + rc = check_nnp_nosuid(bprm, old_tsec, new_tsec); + if (rc) + return rc; } else { /* Check for a default transition on this program. */ rc = security_transition_sid(old_tsec->sid, isec->sid, @@ -2148,15 +2179,19 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) &new_tsec->sid); if (rc) return rc; + + /* + * Fallback to old SID on NNP or nosuid if not an allowed + * transition. + */ + rc = check_nnp_nosuid(bprm, old_tsec, new_tsec); + if (rc) + new_tsec->sid = old_tsec->sid; } ad.type = LSM_AUDIT_DATA_PATH; ad.u.path = bprm->file->f_path; - if ((bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) || - (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) - new_tsec->sid = old_tsec->sid; - if (new_tsec->sid == old_tsec->sid) { rc = avc_has_perm(old_tsec->sid, isec->sid, SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad); -- cgit v1.2.3 From d166c8024d620d654b12834fac354fb4203c6c22 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Wed, 27 Aug 2014 14:51:27 -0700 Subject: Smack: Bring-up access mode People keep asking me for permissive mode, and I keep saying "no". Permissive mode is wrong for more reasons than I can enumerate, but the compelling one is that it's once on, never off. Nonetheless, there is an argument to be made for running a process with lots of permissions, logging which are required, and then locking the process down. There wasn't a way to do that with Smack, but this provides it. The notion is that you start out by giving the process an appropriate Smack label, such as "ATBirds". You create rules with a wide range of access and the "b" mode. On Tizen it might be: ATBirds System rwxalb ATBirds User rwxalb ATBirds _ rwxalb User ATBirds wb System ATBirds wb Accesses that fail will generate audit records. Accesses that succeed because of rules marked with a "b" generate log messages identifying the rule, the program and as much object information as is convenient. When the system is properly configured and the programs brought in line with the labeling scheme the "b" mode can be removed from the rules. When the system is ready for production the facility can be configured out. This provides the developer the convenience of permissive mode without creating a system that looks like it is enforcing a policy while it is not. Signed-off-by: Casey Schaufler --- security/smack/Kconfig | 16 +++ security/smack/smack.h | 5 +- security/smack/smack_access.c | 24 +++- security/smack/smack_lsm.c | 265 ++++++++++++++++++++++++++++++++++++++---- security/smack/smackfs.c | 11 +- 5 files changed, 294 insertions(+), 27 deletions(-) diff --git a/security/smack/Kconfig b/security/smack/Kconfig index e69de9c642b7..b065f9789418 100644 --- a/security/smack/Kconfig +++ b/security/smack/Kconfig @@ -12,3 +12,19 @@ config SECURITY_SMACK of other mandatory security schemes. If you are unsure how to answer this question, answer N. +config SECURITY_SMACK_BRINGUP + bool "Reporting on access granted by Smack rules" + depends on SECURITY_SMACK + default n + help + Enable the bring-up ("b") access mode in Smack rules. + When access is granted by a rule with the "b" mode a + message about the access requested is generated. The + intention is that a process can be granted a wide set + of access initially with the bringup mode set on the + rules. The developer can use the information to + identify which rules are necessary and what accesses + may be inappropriate. The developer can reduce the + access rule set once the behavior is well understood. + This is a superior mechanism to the oft abused + "permissive" mode of other systems. diff --git a/security/smack/smack.h b/security/smack/smack.h index 020307ef0972..2d13d5fb17ed 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -191,6 +191,7 @@ struct smk_port_label { */ #define MAY_TRANSMUTE 0x00001000 /* Controls directory labeling */ #define MAY_LOCK 0x00002000 /* Locks should be writes, but ... */ +#define MAY_BRINGUP 0x00004000 /* Report use of this rule */ /* * Just to make the common cases easier to deal with @@ -200,9 +201,9 @@ struct smk_port_label { #define MAY_NOT 0 /* - * Number of access types used by Smack (rwxatl) + * Number of access types used by Smack (rwxatlb) */ -#define SMK_NUM_ACCESS_TYPE 6 +#define SMK_NUM_ACCESS_TYPE 7 /* SMACK data */ struct smack_audit_data { diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index f97d0842e621..9f02cb0ac85e 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -178,16 +178,27 @@ int smk_access(struct smack_known *subject_known, char *object_label, &subject_known->smk_rules); rcu_read_unlock(); - if (may > 0 && (request & may) == request) + if (may <= 0 || (request & may) != request) { + rc = -EACCES; goto out_audit; + } +#ifdef CONFIG_SECURITY_SMACK_BRINGUP + /* + * Return a positive value if using bringup mode. + * This allows the hooks to identify checks that + * succeed because of "b" rules. + */ + if (may & MAY_BRINGUP) + rc = MAY_BRINGUP; +#endif - rc = -EACCES; out_audit: #ifdef CONFIG_AUDIT if (a) smack_log(subject_known->smk_known, object_label, request, rc, a); #endif + return rc; } @@ -214,7 +225,7 @@ int smk_tskacc(struct task_smack *subject, char *obj_label, * Check the global rule list */ rc = smk_access(skp, obj_label, mode, NULL); - if (rc == 0) { + if (rc >= 0) { /* * If there is an entry in the task's rule list * it can further restrict access. @@ -328,6 +339,13 @@ void smack_log(char *subject_label, char *object_label, int request, struct smack_audit_data *sad; struct common_audit_data *a = &ad->a; +#ifdef CONFIG_SECURITY_SMACK_BRINGUP + /* + * The result may be positive in bringup mode. + */ + if (result > 0) + result = 0; +#endif /* check if we have to log the current event */ if (result != 0 && (log_policy & SMACK_AUDIT_DENIED) == 0) return; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 7091b46adab2..154548ea7d87 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -54,6 +54,149 @@ LIST_HEAD(smk_ipv6_port_list); +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +static void smk_bu_mode(int mode, char *s) +{ + int i = 0; + + if (mode & MAY_READ) + s[i++] = 'r'; + if (mode & MAY_WRITE) + s[i++] = 'w'; + if (mode & MAY_EXEC) + s[i++] = 'x'; + if (mode & MAY_APPEND) + s[i++] = 'a'; + if (mode & MAY_TRANSMUTE) + s[i++] = 't'; + if (mode & MAY_LOCK) + s[i++] = 'l'; + if (i == 0) + s[i++] = '-'; + s[i] = '\0'; +} +#endif + +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +static int smk_bu_note(char *note, struct smack_known *sskp, char *osp, + int mode, int rc) +{ + char acc[SMK_NUM_ACCESS_TYPE + 1]; + + if (rc <= 0) + return rc; + + smk_bu_mode(mode, acc); + pr_info("Smack Bringup: (%s %s %s) %s\n", + sskp->smk_known, osp, acc, note); + return 0; +} +#else +#define smk_bu_note(note, sskp, osp, mode, RC) (RC) +#endif + +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +static int smk_bu_current(char *note, char *osp, int mode, int rc) +{ + struct task_smack *tsp = current_security(); + char acc[SMK_NUM_ACCESS_TYPE + 1]; + + if (rc <= 0) + return rc; + + smk_bu_mode(mode, acc); + pr_info("Smack Bringup: (%s %s %s) %s %s\n", + tsp->smk_task->smk_known, osp, acc, current->comm, note); + return 0; +} +#else +#define smk_bu_current(note, osp, mode, RC) (RC) +#endif + +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +static int smk_bu_task(struct task_struct *otp, int mode, int rc) +{ + struct task_smack *tsp = current_security(); + struct task_smack *otsp = task_security(otp); + char acc[SMK_NUM_ACCESS_TYPE + 1]; + + if (rc <= 0) + return rc; + + smk_bu_mode(mode, acc); + pr_info("Smack Bringup: (%s %s %s) %s to %s\n", + tsp->smk_task->smk_known, otsp->smk_task->smk_known, acc, + current->comm, otp->comm); + return 0; +} +#else +#define smk_bu_task(otp, mode, RC) (RC) +#endif + +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +static int smk_bu_inode(struct inode *inode, int mode, int rc) +{ + struct task_smack *tsp = current_security(); + char acc[SMK_NUM_ACCESS_TYPE + 1]; + + if (rc <= 0) + return rc; + + smk_bu_mode(mode, acc); + pr_info("Smack Bringup: (%s %s %s) inode=(%s %ld) %s\n", + tsp->smk_task->smk_known, smk_of_inode(inode), acc, + inode->i_sb->s_id, inode->i_ino, current->comm); + return 0; +} +#else +#define smk_bu_inode(inode, mode, RC) (RC) +#endif + +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +static int smk_bu_file(struct file *file, int mode, int rc) +{ + struct task_smack *tsp = current_security(); + struct smack_known *sskp = tsp->smk_task; + struct inode *inode = file->f_inode; + char acc[SMK_NUM_ACCESS_TYPE + 1]; + + if (rc <= 0) + return rc; + + smk_bu_mode(mode, acc); + pr_info("Smack Bringup: (%s %s %s) file=(%s %ld %s) %s\n", + sskp->smk_known, (char *)file->f_security, acc, + inode->i_sb->s_id, inode->i_ino, file->f_dentry->d_name.name, + current->comm); + return 0; +} +#else +#define smk_bu_file(file, mode, RC) (RC) +#endif + +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +static int smk_bu_credfile(const struct cred *cred, struct file *file, + int mode, int rc) +{ + struct task_smack *tsp = cred->security; + struct smack_known *sskp = tsp->smk_task; + struct inode *inode = file->f_inode; + char acc[SMK_NUM_ACCESS_TYPE + 1]; + + if (rc <= 0) + return rc; + + smk_bu_mode(mode, acc); + pr_info("Smack Bringup: (%s %s %s) file=(%s %ld %s) %s\n", + sskp->smk_known, smk_of_inode(inode), acc, + inode->i_sb->s_id, inode->i_ino, file->f_dentry->d_name.name, + current->comm); + return 0; +} +#else +#define smk_bu_credfile(cred, file, mode, RC) (RC) +#endif + /** * smk_fetch - Fetch the smack label from a file. * @ip: a pointer to the inode @@ -507,6 +650,7 @@ static int smack_sb_statfs(struct dentry *dentry) smk_ad_setfield_u_fs_path_dentry(&ad, dentry); rc = smk_curacc(sbp->smk_floor, MAY_READ, &ad); + rc = smk_bu_current("statfs", sbp->smk_floor, MAY_READ, rc); return rc; } @@ -697,11 +841,13 @@ static int smack_inode_link(struct dentry *old_dentry, struct inode *dir, isp = smk_of_inode(old_dentry->d_inode); rc = smk_curacc(isp, MAY_WRITE, &ad); + rc = smk_bu_inode(old_dentry->d_inode, MAY_WRITE, rc); if (rc == 0 && new_dentry->d_inode != NULL) { isp = smk_of_inode(new_dentry->d_inode); smk_ad_setfield_u_fs_path_dentry(&ad, new_dentry); rc = smk_curacc(isp, MAY_WRITE, &ad); + rc = smk_bu_inode(new_dentry->d_inode, MAY_WRITE, rc); } return rc; @@ -728,6 +874,7 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) * You need write access to the thing you're unlinking */ rc = smk_curacc(smk_of_inode(ip), MAY_WRITE, &ad); + rc = smk_bu_inode(ip, MAY_WRITE, rc); if (rc == 0) { /* * You also need write access to the containing directory @@ -735,6 +882,7 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) 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); + rc = smk_bu_inode(dir, MAY_WRITE, rc); } return rc; } @@ -759,6 +907,7 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) * You need write access to the thing you're removing */ rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); + rc = smk_bu_inode(dentry->d_inode, MAY_WRITE, rc); if (rc == 0) { /* * You also need write access to the containing directory @@ -766,6 +915,7 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) 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); + rc = smk_bu_inode(dir, MAY_WRITE, rc); } return rc; @@ -797,11 +947,13 @@ static int smack_inode_rename(struct inode *old_inode, isp = smk_of_inode(old_dentry->d_inode); rc = smk_curacc(isp, MAY_READWRITE, &ad); + rc = smk_bu_inode(old_dentry->d_inode, MAY_READWRITE, rc); if (rc == 0 && new_dentry->d_inode != NULL) { isp = smk_of_inode(new_dentry->d_inode); smk_ad_setfield_u_fs_path_dentry(&ad, new_dentry); rc = smk_curacc(isp, MAY_READWRITE, &ad); + rc = smk_bu_inode(new_dentry->d_inode, MAY_READWRITE, rc); } return rc; } @@ -819,6 +971,7 @@ static int smack_inode_permission(struct inode *inode, int mask) { struct smk_audit_info ad; int no_block = mask & MAY_NOT_BLOCK; + int rc; mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); /* @@ -832,7 +985,9 @@ static int smack_inode_permission(struct inode *inode, int mask) return -ECHILD; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE); smk_ad_setfield_u_fs_inode(&ad, inode); - return smk_curacc(smk_of_inode(inode), mask, &ad); + rc = smk_curacc(smk_of_inode(inode), mask, &ad); + rc = smk_bu_inode(inode, mask, rc); + return rc; } /** @@ -845,6 +1000,8 @@ static int smack_inode_permission(struct inode *inode, int mask) static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) { struct smk_audit_info ad; + int rc; + /* * Need to allow for clearing the setuid bit. */ @@ -853,7 +1010,9 @@ static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); - return smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); + rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); + rc = smk_bu_inode(dentry->d_inode, MAY_WRITE, rc); + return rc; } /** @@ -867,13 +1026,16 @@ static int smack_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) { struct smk_audit_info ad; struct path path; + int rc; path.dentry = dentry; path.mnt = mnt; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, path); - return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad); + rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad); + rc = smk_bu_inode(dentry->d_inode, MAY_READ, rc); + return rc; } /** @@ -932,8 +1094,10 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); - if (rc == 0) + if (rc == 0) { rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); + rc = smk_bu_inode(dentry->d_inode, MAY_WRITE, rc); + } return rc; } @@ -993,11 +1157,14 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, static int smack_inode_getxattr(struct dentry *dentry, const char *name) { struct smk_audit_info ad; + int rc; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); - return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad); + rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad); + rc = smk_bu_inode(dentry->d_inode, MAY_READ, rc); + return rc; } /** @@ -1033,6 +1200,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) smk_ad_setfield_u_fs_path_dentry(&ad, dentry); rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); + rc = smk_bu_inode(dentry->d_inode, MAY_WRITE, rc); if (rc != 0) return rc; @@ -1213,11 +1381,15 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd, smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); - if (_IOC_DIR(cmd) & _IOC_WRITE) + if (_IOC_DIR(cmd) & _IOC_WRITE) { rc = smk_curacc(file->f_security, MAY_WRITE, &ad); + rc = smk_bu_file(file, MAY_WRITE, rc); + } - if (rc == 0 && (_IOC_DIR(cmd) & _IOC_READ)) + if (rc == 0 && (_IOC_DIR(cmd) & _IOC_READ)) { rc = smk_curacc(file->f_security, MAY_READ, &ad); + rc = smk_bu_file(file, MAY_READ, rc); + } return rc; } @@ -1232,10 +1404,13 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd, static int smack_file_lock(struct file *file, unsigned int cmd) { struct smk_audit_info ad; + int rc; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); - return smk_curacc(file->f_security, MAY_LOCK, &ad); + rc = smk_curacc(file->f_security, MAY_LOCK, &ad); + rc = smk_bu_file(file, MAY_LOCK, rc); + return rc; } /** @@ -1265,12 +1440,14 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd, smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); rc = smk_curacc(file->f_security, MAY_LOCK, &ad); + rc = smk_bu_file(file, MAY_LOCK, rc); break; case F_SETOWN: case F_SETSIG: smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); rc = smk_curacc(file->f_security, MAY_WRITE, &ad); + rc = smk_bu_file(file, MAY_WRITE, rc); break; default: break; @@ -1425,6 +1602,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, /* we don't log here as rc can be overriden */ skp = smk_find_entry(file->f_security); rc = smk_access(skp, tkp->smk_known, MAY_WRITE, NULL); + rc = smk_bu_note("sigiotask", skp, tkp->smk_known, MAY_WRITE, rc); if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE)) rc = 0; @@ -1442,6 +1620,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, */ static int smack_file_receive(struct file *file) { + int rc; int may = 0; struct smk_audit_info ad; @@ -1455,7 +1634,9 @@ static int smack_file_receive(struct file *file) if (file->f_mode & FMODE_WRITE) may |= MAY_WRITE; - return smk_curacc(file->f_security, may, &ad); + rc = smk_curacc(file->f_security, may, &ad); + rc = smk_bu_file(file, may, rc); + return rc; } /** @@ -1485,6 +1666,7 @@ static int smack_file_open(struct file *file, const struct cred *cred) smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); rc = smk_access(tsp->smk_task, isp->smk_inode, MAY_READ, &ad); + rc = smk_bu_credfile(cred, file, MAY_READ, rc); if (rc == 0) file->f_security = isp->smk_inode; @@ -1641,10 +1823,13 @@ static int smk_curacc_on_task(struct task_struct *p, int access, { struct smk_audit_info ad; struct smack_known *skp = smk_of_task(task_security(p)); + int rc; smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, p); - return smk_curacc(skp->smk_known, access, &ad); + rc = smk_curacc(skp->smk_known, access, &ad); + rc = smk_bu_task(p, access, rc); + return rc; } /** @@ -1798,6 +1983,7 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, struct smk_audit_info ad; struct smack_known *skp; struct smack_known *tkp = smk_of_task(task_security(p)); + int rc; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, p); @@ -1805,15 +1991,20 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, * Sending a signal requires that the sender * can write the receiver. */ - if (secid == 0) - return smk_curacc(tkp->smk_known, MAY_WRITE, &ad); + if (secid == 0) { + rc = smk_curacc(tkp->smk_known, MAY_WRITE, &ad); + rc = smk_bu_task(p, MAY_WRITE, rc); + return rc; + } /* * If the secid isn't 0 we're dealing with some USB IO * specific behavior. This is not clean. For one thing * we can't take privilege into account. */ skp = smack_from_secid(secid); - return smk_access(skp, tkp->smk_known, MAY_WRITE, &ad); + rc = smk_access(skp, tkp->smk_known, MAY_WRITE, &ad); + rc = smk_bu_note("USB signal", skp, tkp->smk_known, MAY_WRITE, rc); + return rc; } /** @@ -2005,6 +2196,7 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) sk_lbl = SMACK_UNLABELED_SOCKET; skp = ssp->smk_out; rc = smk_access(skp, hostsp, MAY_WRITE, &ad); + rc = smk_bu_note("IPv4 host check", skp, hostsp, MAY_WRITE, rc); } else { sk_lbl = SMACK_CIPSO_SOCKET; rc = 0; @@ -2107,6 +2299,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, unsigned short port = 0; char *object; struct smk_audit_info ad; + int rc; #ifdef CONFIG_AUDIT struct lsm_network_audit net; #endif @@ -2160,7 +2353,9 @@ auditout: else ad.a.u.net->v6info.daddr = address->sin6_addr; #endif - return smk_access(skp, object, MAY_WRITE, &ad); + rc = smk_access(skp, object, MAY_WRITE, &ad); + rc = smk_bu_note("IPv6 port check", skp, object, MAY_WRITE, rc); + return rc; } /** @@ -2399,12 +2594,15 @@ static int smk_curacc_shm(struct shmid_kernel *shp, int access) { char *ssp = smack_of_shm(shp); struct smk_audit_info ad; + int rc; #ifdef CONFIG_AUDIT smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); ad.a.u.ipc_id = shp->shm_perm.id; #endif - return smk_curacc(ssp, access, &ad); + rc = smk_curacc(ssp, access, &ad); + rc = smk_bu_current("shm", ssp, access, rc); + return rc; } /** @@ -2523,12 +2721,15 @@ static int smk_curacc_sem(struct sem_array *sma, int access) { char *ssp = smack_of_sem(sma); struct smk_audit_info ad; + int rc; #ifdef CONFIG_AUDIT smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); ad.a.u.ipc_id = sma->sem_perm.id; #endif - return smk_curacc(ssp, access, &ad); + rc = smk_curacc(ssp, access, &ad); + rc = smk_bu_current("sem", ssp, access, rc); + return rc; } /** @@ -2653,12 +2854,15 @@ static int smk_curacc_msq(struct msg_queue *msq, int access) { char *msp = smack_of_msq(msq); struct smk_audit_info ad; + int rc; #ifdef CONFIG_AUDIT smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); ad.a.u.ipc_id = msq->q_perm.id; #endif - return smk_curacc(msp, access, &ad); + rc = smk_curacc(msp, access, &ad); + rc = smk_bu_current("msq", msp, access, rc); + return rc; } /** @@ -2754,12 +2958,15 @@ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) char *isp = ipp->security; int may = smack_flags_to_may(flag); struct smk_audit_info ad; + int rc; #ifdef CONFIG_AUDIT smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); ad.a.u.ipc_id = ipp->id; #endif - return smk_curacc(isp, may, &ad); + rc = smk_curacc(isp, may, &ad); + rc = smk_bu_current("svipc", isp, may, rc); + return rc; } /** @@ -3092,8 +3299,13 @@ static int smack_unix_stream_connect(struct sock *sock, smk_ad_setfield_u_net_sk(&ad, other); #endif rc = smk_access(skp, okp->smk_known, MAY_WRITE, &ad); - if (rc == 0) + rc = smk_bu_note("UDS connect", skp, okp->smk_known, + MAY_WRITE, rc); + if (rc == 0) { rc = smk_access(okp, okp->smk_known, MAY_WRITE, NULL); + rc = smk_bu_note("UDS connect", okp, okp->smk_known, + MAY_WRITE, rc); + } } /* @@ -3121,6 +3333,7 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) struct socket_smack *osp = other->sk->sk_security; struct smack_known *skp; struct smk_audit_info ad; + int rc; #ifdef CONFIG_AUDIT struct lsm_network_audit net; @@ -3133,7 +3346,10 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) return 0; skp = ssp->smk_out; - return smk_access(skp, osp->smk_in->smk_known, MAY_WRITE, &ad); + rc = smk_access(skp, osp->smk_in->smk_known, MAY_WRITE, &ad); + rc = smk_bu_note("UDS send", skp, osp->smk_in->smk_known, + MAY_WRITE, rc); + return rc; } /** @@ -3348,6 +3564,8 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) * for networking. */ rc = smk_access(skp, ssp->smk_in->smk_known, MAY_WRITE, &ad); + rc = smk_bu_note("IPv4 delivery", skp, ssp->smk_in->smk_known, + MAY_WRITE, rc); if (rc != 0) netlbl_skbuff_err(skb, rc, 0); break; @@ -3528,6 +3746,8 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, * here. Read access is not required. */ rc = smk_access(skp, ssp->smk_in->smk_known, MAY_WRITE, &ad); + rc = smk_bu_note("IPv4 connect", skp, ssp->smk_in->smk_known, + MAY_WRITE, rc); if (rc != 0) return rc; @@ -3631,6 +3851,7 @@ static int smack_key_permission(key_ref_t key_ref, struct smk_audit_info ad; struct smack_known *tkp = smk_of_task(cred->security); int request = 0; + int rc; keyp = key_ref_to_ptr(key_ref); if (keyp == NULL) @@ -3655,7 +3876,9 @@ static int smack_key_permission(key_ref_t key_ref, request = MAY_READ; if (perm & (KEY_NEED_WRITE | KEY_NEED_LINK | KEY_NEED_SETATTR)) request = MAY_WRITE; - return smk_access(tkp, keyp->security, request, &ad); + rc = smk_access(tkp, keyp->security, request, &ad); + rc = smk_bu_note("key access", tkp, keyp->security, request, rc); + return rc; } #endif /* CONFIG_KEYS */ diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 56a1439786a9..49a2248b525c 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -304,6 +304,10 @@ static int smk_perm_from_str(const char *string) case 'L': perm |= MAY_LOCK; break; + case 'b': + case 'B': + perm |= MAY_BRINGUP; + break; default: return perm; } @@ -616,6 +620,8 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) seq_putc(s, 't'); if (srp->smk_access & MAY_LOCK) seq_putc(s, 'l'); + if (srp->smk_access & MAY_BRINGUP) + seq_putc(s, 'b'); seq_putc(s, '\n'); } @@ -1880,7 +1886,10 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, else if (res != -ENOENT) return -EINVAL; - data[0] = res == 0 ? '1' : '0'; + /* + * smk_access() can return a value > 0 in the "bringup" case. + */ + data[0] = res >= 0 ? '1' : '0'; data[1] = '\0'; simple_transaction_set(file, 2); -- cgit v1.2.3 From e95ef49b7f8f497bdb529f4cb1fe228e986b3255 Mon Sep 17 00:00:00 2001 From: Lukasz Pawelczyk Date: Fri, 29 Aug 2014 17:02:53 +0200 Subject: Small fixes in comments describing function parameters Signed-off-by: Lukasz Pawelczyk --- security/smack/smack_lsm.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 154548ea7d87..478d99e0a0eb 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -771,8 +771,8 @@ static void smack_inode_free_security(struct inode *inode) /** * smack_inode_init_security - copy out the smack from an inode - * @inode: the inode - * @dir: unused + * @inode: the newly created inode + * @dir: containing directory object * @qstr: unused * @name: where to put the attribute name * @value: where to put the attribute value @@ -923,10 +923,10 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) /** * smack_inode_rename - Smack check on rename - * @old_inode: the old directory - * @old_dentry: unused - * @new_inode: the new directory - * @new_dentry: unused + * @old_inode: unused + * @old_dentry: the old object + * @new_inode: unused + * @new_dentry: the new object * * Read and write access is required on both the old and * new directories. @@ -1017,7 +1017,7 @@ static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) /** * smack_inode_getattr - Smack check for getting attributes - * @mnt: unused + * @mnt: vfsmount of the object * @dentry: the object * * Returns 0 if access is permitted, an error code otherwise @@ -1042,8 +1042,8 @@ static int smack_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) * smack_inode_setxattr - Smack check for setting xattrs * @dentry: the object * @name: name of the attribute - * @value: unused - * @size: unused + * @value: value of the attribute + * @size: size of the value * @flags: unused * * This protects the Smack attribute explicitly. -- cgit v1.2.3 From d01757904d9deb619e23c9450218829943a46822 Mon Sep 17 00:00:00 2001 From: Lukasz Pawelczyk Date: Fri, 29 Aug 2014 17:02:54 +0200 Subject: Fix a bidirectional UDS connect check typo The 54e70ec5eb090193b03e69d551fa6771a5a217c4 commit introduced a bidirectional check that should have checked for mutual WRITE access between two labels. Due to a typo the second check was incorrect. Signed-off-by: Lukasz Pawelczyk --- 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 478d99e0a0eb..00443a9039b8 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3302,8 +3302,8 @@ static int smack_unix_stream_connect(struct sock *sock, rc = smk_bu_note("UDS connect", skp, okp->smk_known, MAY_WRITE, rc); if (rc == 0) { - rc = smk_access(okp, okp->smk_known, MAY_WRITE, NULL); - rc = smk_bu_note("UDS connect", okp, okp->smk_known, + rc = smk_access(okp, skp->smk_known, MAY_WRITE, NULL); + rc = smk_bu_note("UDS connect", okp, skp->smk_known, MAY_WRITE, rc); } } -- cgit v1.2.3 From 21c7eae21a2100a89cfb8cebaf7b770271f32c6e Mon Sep 17 00:00:00 2001 From: Lukasz Pawelczyk Date: Fri, 29 Aug 2014 17:02:55 +0200 Subject: Make Smack operate on smack_known struct where it still used char* Smack used to use a mix of smack_known struct and char* throughout its APIs and implementation. This patch unifies the behaviour and makes it store and operate exclusively on smack_known struct pointers when managing labels. Signed-off-by: Lukasz Pawelczyk Conflicts: security/smack/smack_access.c security/smack/smack_lsm.c --- security/smack/smack.h | 34 ++--- security/smack/smack_access.c | 94 +++++-------- security/smack/smack_lsm.c | 299 +++++++++++++++++++++--------------------- security/smack/smackfs.c | 61 +++++---- 4 files changed, 233 insertions(+), 255 deletions(-) diff --git a/security/smack/smack.h b/security/smack/smack.h index 2d13d5fb17ed..b828a379377c 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -71,11 +71,11 @@ struct smack_known { #define SMK_CIPSOLEN 24 struct superblock_smack { - char *smk_root; - char *smk_floor; - char *smk_hat; - char *smk_default; - int smk_initialized; + struct smack_known *smk_root; + struct smack_known *smk_floor; + struct smack_known *smk_hat; + struct smack_known *smk_default; + int smk_initialized; }; struct socket_smack { @@ -88,7 +88,7 @@ struct socket_smack { * Inode smack data */ struct inode_smack { - char *smk_inode; /* label of the fso */ + struct smack_known *smk_inode; /* label of the fso */ struct smack_known *smk_task; /* label of the task */ struct smack_known *smk_mmap; /* label of the mmap domain */ struct mutex smk_lock; /* initialization lock */ @@ -112,7 +112,7 @@ struct task_smack { struct smack_rule { struct list_head list; struct smack_known *smk_subject; - char *smk_object; + struct smack_known *smk_object; int smk_access; }; @@ -123,7 +123,7 @@ struct smk_netlbladdr { struct list_head list; struct sockaddr_in smk_host; /* network address */ struct in_addr smk_mask; /* network mask */ - char *smk_label; /* label */ + struct smack_known *smk_label; /* label */ }; /* @@ -227,23 +227,23 @@ struct smk_audit_info { /* * These functions are in smack_lsm.c */ -struct inode_smack *new_inode_smack(char *); +struct inode_smack *new_inode_smack(struct smack_known *); /* * These functions are in smack_access.c */ int smk_access_entry(char *, char *, struct list_head *); -int smk_access(struct smack_known *, char *, int, struct smk_audit_info *); -int smk_tskacc(struct task_smack *, char *, u32, struct smk_audit_info *); -int smk_curacc(char *, u32, struct smk_audit_info *); +int smk_access(struct smack_known *, struct smack_known *, + int, struct smk_audit_info *); +int smk_tskacc(struct task_smack *, struct smack_known *, + u32, struct smk_audit_info *); +int smk_curacc(struct smack_known *, u32, struct smk_audit_info *); struct smack_known *smack_from_secid(const u32); char *smk_parse_smack(const char *string, int len); int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); -char *smk_import(const char *, int); struct smack_known *smk_import_entry(const char *, int); void smk_insert_entry(struct smack_known *skp); struct smack_known *smk_find_entry(const char *); -u32 smack_to_secid(const char *); /* * Shared data. @@ -253,7 +253,7 @@ extern int smack_cipso_mapped; extern struct smack_known *smack_net_ambient; extern struct smack_known *smack_onlycap; extern struct smack_known *smack_syslog_label; -extern const char *smack_cipso_option; +extern struct smack_known smack_cipso_option; extern int smack_ptrace_rule; extern struct smack_known smack_known_floor; @@ -282,9 +282,9 @@ static inline int smk_inode_transmutable(const struct inode *isp) } /* - * Present a pointer to the smack label in an inode blob. + * Present a pointer to the smack label entry in an inode blob. */ -static inline char *smk_of_inode(const struct inode *isp) +static inline struct smack_known *smk_of_inode(const struct inode *isp) { struct inode_smack *sip = isp->i_security; return sip->smk_inode; diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 9f02cb0ac85e..5b970ffde024 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -94,7 +94,7 @@ int smk_access_entry(char *subject_label, char *object_label, struct smack_rule *srp; list_for_each_entry_rcu(srp, rule_list, list) { - if (srp->smk_object == object_label && + if (srp->smk_object->smk_known == object_label && srp->smk_subject->smk_known == subject_label) { may = srp->smk_access; break; @@ -111,8 +111,8 @@ int smk_access_entry(char *subject_label, char *object_label, /** * smk_access - determine if a subject has a specific access to an object - * @subject_known: a pointer to the subject's Smack label entry - * @object_label: a pointer to the object's Smack label + * @subject: a pointer to the subject's Smack label entry + * @object: a pointer to the object's Smack label entry * @request: the access requested, in "MAY" format * @a : a pointer to the audit data * @@ -122,8 +122,8 @@ int smk_access_entry(char *subject_label, char *object_label, * * Smack labels are shared on smack_list */ -int smk_access(struct smack_known *subject_known, char *object_label, - int request, struct smk_audit_info *a) +int smk_access(struct smack_known *subject, struct smack_known *object, + int request, struct smk_audit_info *a) { int may = MAY_NOT; int rc = 0; @@ -133,7 +133,7 @@ int smk_access(struct smack_known *subject_known, char *object_label, * * A star subject can't access any object. */ - if (subject_known == &smack_known_star) { + if (subject == &smack_known_star) { rc = -EACCES; goto out_audit; } @@ -142,28 +142,28 @@ int smk_access(struct smack_known *subject_known, char *object_label, * Tasks cannot be assigned the internet label. * An internet subject can access any object. */ - if (object_label == smack_known_web.smk_known || - subject_known == &smack_known_web) + if (object == &smack_known_web || + subject == &smack_known_web) goto out_audit; /* * A star object can be accessed by any subject. */ - if (object_label == smack_known_star.smk_known) + if (object == &smack_known_star) goto out_audit; /* * An object can be accessed in any way by a subject * with the same label. */ - if (subject_known->smk_known == object_label) + if (subject->smk_known == object->smk_known) goto out_audit; /* * A hat subject can read any object. * A floor object can be read by any subject. */ if ((request & MAY_ANYREAD) == request) { - if (object_label == smack_known_floor.smk_known) + if (object == &smack_known_floor) goto out_audit; - if (subject_known == &smack_known_hat) + if (subject == &smack_known_hat) goto out_audit; } /* @@ -174,8 +174,8 @@ int smk_access(struct smack_known *subject_known, char *object_label, * indicates there is no entry for this pair. */ rcu_read_lock(); - may = smk_access_entry(subject_known->smk_known, object_label, - &subject_known->smk_rules); + may = smk_access_entry(subject->smk_known, object->smk_known, + &subject->smk_rules); rcu_read_unlock(); if (may <= 0 || (request & may) != request) { @@ -195,8 +195,8 @@ int smk_access(struct smack_known *subject_known, char *object_label, out_audit: #ifdef CONFIG_AUDIT if (a) - smack_log(subject_known->smk_known, object_label, request, - rc, a); + smack_log(subject->smk_known, object->smk_known, + request, rc, a); #endif return rc; @@ -204,8 +204,8 @@ out_audit: /** * smk_tskacc - determine if a task has a specific access to an object - * @tsp: a pointer to the subject task - * @obj_label: a pointer to the object's Smack label + * @tsp: a pointer to the subject's task + * @obj_known: a pointer to the object's label entry * @mode: the access requested, in "MAY" format * @a : common audit data * @@ -214,24 +214,25 @@ out_audit: * non zero otherwise. It allows that the task may have the capability * to override the rules. */ -int smk_tskacc(struct task_smack *subject, char *obj_label, +int smk_tskacc(struct task_smack *tsp, struct smack_known *obj_known, u32 mode, struct smk_audit_info *a) { - struct smack_known *skp = smk_of_task(subject); + struct smack_known *sbj_known = smk_of_task(tsp); int may; int rc; /* * Check the global rule list */ - rc = smk_access(skp, obj_label, mode, NULL); + rc = smk_access(sbj_known, obj_known, mode, NULL); if (rc >= 0) { /* * If there is an entry in the task's rule list * it can further restrict access. */ - may = smk_access_entry(skp->smk_known, obj_label, - &subject->smk_rules); + may = smk_access_entry(sbj_known->smk_known, + obj_known->smk_known, + &tsp->smk_rules); if (may < 0) goto out_audit; if ((mode & may) == mode) @@ -248,14 +249,15 @@ int smk_tskacc(struct task_smack *subject, char *obj_label, out_audit: #ifdef CONFIG_AUDIT if (a) - smack_log(skp->smk_known, obj_label, mode, rc, a); + smack_log(sbj_known->smk_known, obj_known->smk_known, + mode, rc, a); #endif return rc; } /** * smk_curacc - determine if current has a specific access to an object - * @obj_label: a pointer to the object's Smack label + * @obj_known: a pointer to the object's Smack label entry * @mode: the access requested, in "MAY" format * @a : common audit data * @@ -264,11 +266,12 @@ out_audit: * non zero otherwise. It allows that current may have the capability * to override the rules. */ -int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) +int smk_curacc(struct smack_known *obj_known, + u32 mode, struct smk_audit_info *a) { struct task_smack *tsp = current_security(); - return smk_tskacc(tsp, obj_label, mode, a); + return smk_tskacc(tsp, obj_known, mode, a); } #ifdef CONFIG_AUDIT @@ -561,27 +564,6 @@ unlockout: return skp; } -/** - * smk_import - import a smack label - * @string: a text string that might be a Smack label - * @len: the maximum size, or zero if it is NULL terminated. - * - * Returns a pointer to the label in the label list that - * matches the passed string, adding it if necessary. - */ -char *smk_import(const char *string, int len) -{ - struct smack_known *skp; - - /* labels cannot begin with a '-' */ - if (string[0] == '-') - return NULL; - skp = smk_import_entry(string, len); - if (skp == NULL) - return NULL; - return skp->smk_known; -} - /** * smack_from_secid - find the Smack label associated with a secid * @secid: an integer that might be associated with a Smack label @@ -608,19 +590,3 @@ struct smack_known *smack_from_secid(const u32 secid) rcu_read_unlock(); return &smack_known_invalid; } - -/** - * smack_to_secid - find the secid associated with a Smack label - * @smack: the Smack label - * - * Returns the appropriate secid if there is one, - * otherwise 0 - */ -u32 smack_to_secid(const char *smack) -{ - struct smack_known *skp = smk_find_entry(smack); - - if (skp == NULL) - return 0; - return skp->smk_secid; -} diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 00443a9039b8..93dc876734a4 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -78,8 +78,8 @@ static void smk_bu_mode(int mode, char *s) #endif #ifdef CONFIG_SECURITY_SMACK_BRINGUP -static int smk_bu_note(char *note, struct smack_known *sskp, char *osp, - int mode, int rc) +static int smk_bu_note(char *note, struct smack_known *sskp, + struct smack_known *oskp, int mode, int rc) { char acc[SMK_NUM_ACCESS_TYPE + 1]; @@ -88,15 +88,16 @@ static int smk_bu_note(char *note, struct smack_known *sskp, char *osp, smk_bu_mode(mode, acc); pr_info("Smack Bringup: (%s %s %s) %s\n", - sskp->smk_known, osp, acc, note); + sskp->smk_known, oskp->smk_known, acc, note); return 0; } #else -#define smk_bu_note(note, sskp, osp, mode, RC) (RC) +#define smk_bu_note(note, sskp, oskp, mode, RC) (RC) #endif #ifdef CONFIG_SECURITY_SMACK_BRINGUP -static int smk_bu_current(char *note, char *osp, int mode, int rc) +static int smk_bu_current(char *note, struct smack_known *oskp, + int mode, int rc) { struct task_smack *tsp = current_security(); char acc[SMK_NUM_ACCESS_TYPE + 1]; @@ -106,11 +107,12 @@ static int smk_bu_current(char *note, char *osp, int mode, int rc) smk_bu_mode(mode, acc); pr_info("Smack Bringup: (%s %s %s) %s %s\n", - tsp->smk_task->smk_known, osp, acc, current->comm, note); + tsp->smk_task->smk_known, oskp->smk_known, + acc, current->comm, note); return 0; } #else -#define smk_bu_current(note, osp, mode, RC) (RC) +#define smk_bu_current(note, oskp, mode, RC) (RC) #endif #ifdef CONFIG_SECURITY_SMACK_BRINGUP @@ -144,7 +146,7 @@ static int smk_bu_inode(struct inode *inode, int mode, int rc) smk_bu_mode(mode, acc); pr_info("Smack Bringup: (%s %s %s) inode=(%s %ld) %s\n", - tsp->smk_task->smk_known, smk_of_inode(inode), acc, + tsp->smk_task->smk_known, smk_of_inode(inode)->smk_known, acc, inode->i_sb->s_id, inode->i_ino, current->comm); return 0; } @@ -188,7 +190,7 @@ static int smk_bu_credfile(const struct cred *cred, struct file *file, smk_bu_mode(mode, acc); pr_info("Smack Bringup: (%s %s %s) file=(%s %ld %s) %s\n", - sskp->smk_known, smk_of_inode(inode), acc, + sskp->smk_known, smk_of_inode(inode)->smk_known, acc, inode->i_sb->s_id, inode->i_ino, file->f_dentry->d_name.name, current->comm); return 0; @@ -230,11 +232,11 @@ static struct smack_known *smk_fetch(const char *name, struct inode *ip, /** * new_inode_smack - allocate an inode security blob - * @smack: a pointer to the Smack label to use in the blob + * @skp: a pointer to the Smack label entry to use in the blob * * Returns the new blob or NULL if there's no memory available */ -struct inode_smack *new_inode_smack(char *smack) +struct inode_smack *new_inode_smack(struct smack_known *skp) { struct inode_smack *isp; @@ -242,7 +244,7 @@ struct inode_smack *new_inode_smack(char *smack) if (isp == NULL) return NULL; - isp->smk_inode = smack; + isp->smk_inode = skp; isp->smk_flags = 0; mutex_init(&isp->smk_lock); @@ -321,20 +323,20 @@ static inline unsigned int smk_ptrace_mode(unsigned int mode) /** * smk_ptrace_rule_check - helper for ptrace access * @tracer: tracer process - * @tracee_label: label of the process that's about to be traced, - * the pointer must originate from smack structures + * @tracee_known: label entry of the process that's about to be traced * @mode: ptrace attachment mode (PTRACE_MODE_*) * @func: name of the function that called us, used for audit * * Returns 0 on access granted, -error on error */ -static int smk_ptrace_rule_check(struct task_struct *tracer, char *tracee_label, +static int smk_ptrace_rule_check(struct task_struct *tracer, + struct smack_known *tracee_known, unsigned int mode, const char *func) { int rc; struct smk_audit_info ad, *saip = NULL; struct task_smack *tsp; - struct smack_known *skp; + struct smack_known *tracer_known; if ((mode & PTRACE_MODE_NOAUDIT) == 0) { smk_ad_init(&ad, func, LSM_AUDIT_DATA_TASK); @@ -343,12 +345,12 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, char *tracee_label, } tsp = task_security(tracer); - skp = smk_of_task(tsp); + tracer_known = smk_of_task(tsp); if ((mode & PTRACE_MODE_ATTACH) && (smack_ptrace_rule == SMACK_PTRACE_EXACT || smack_ptrace_rule == SMACK_PTRACE_DRACONIAN)) { - if (skp->smk_known == tracee_label) + if (tracer_known->smk_known == tracee_known->smk_known) rc = 0; else if (smack_ptrace_rule == SMACK_PTRACE_DRACONIAN) rc = -EACCES; @@ -358,13 +360,15 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, char *tracee_label, rc = -EACCES; if (saip) - smack_log(skp->smk_known, tracee_label, 0, rc, saip); + smack_log(tracer_known->smk_known, + tracee_known->smk_known, + 0, rc, saip); return rc; } /* In case of rule==SMACK_PTRACE_DEFAULT or mode==PTRACE_MODE_READ */ - rc = smk_tskacc(tsp, tracee_label, smk_ptrace_mode(mode), saip); + rc = smk_tskacc(tsp, tracee_known, smk_ptrace_mode(mode), saip); return rc; } @@ -393,7 +397,7 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) skp = smk_of_task(task_security(ctp)); - rc = smk_ptrace_rule_check(current, skp->smk_known, mode, __func__); + rc = smk_ptrace_rule_check(current, skp, mode, __func__); return rc; } @@ -416,8 +420,7 @@ static int smack_ptrace_traceme(struct task_struct *ptp) skp = smk_of_task(current_security()); - rc = smk_ptrace_rule_check(ptp, skp->smk_known, - PTRACE_MODE_ATTACH, __func__); + rc = smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__); return rc; } @@ -461,10 +464,10 @@ static int smack_sb_alloc_security(struct super_block *sb) if (sbsp == NULL) return -ENOMEM; - sbsp->smk_root = smack_known_floor.smk_known; - sbsp->smk_default = smack_known_floor.smk_known; - sbsp->smk_floor = smack_known_floor.smk_known; - sbsp->smk_hat = smack_known_hat.smk_known; + sbsp->smk_root = &smack_known_floor; + sbsp->smk_default = &smack_known_floor; + sbsp->smk_floor = &smack_known_floor; + sbsp->smk_hat = &smack_known_hat; /* * smk_initialized will be zero from kzalloc. */ @@ -548,7 +551,6 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) struct smack_known *skp; char *op; char *commap; - char *nsp; int transmute = 0; int specified = 0; @@ -564,38 +566,38 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) if (strncmp(op, SMK_FSHAT, strlen(SMK_FSHAT)) == 0) { op += strlen(SMK_FSHAT); - nsp = smk_import(op, 0); - if (nsp != NULL) { - sp->smk_hat = nsp; + skp = smk_import_entry(op, 0); + if (skp != NULL) { + sp->smk_hat = skp; specified = 1; } } else if (strncmp(op, SMK_FSFLOOR, strlen(SMK_FSFLOOR)) == 0) { op += strlen(SMK_FSFLOOR); - nsp = smk_import(op, 0); - if (nsp != NULL) { - sp->smk_floor = nsp; + skp = smk_import_entry(op, 0); + if (skp != NULL) { + sp->smk_floor = skp; specified = 1; } } else if (strncmp(op, SMK_FSDEFAULT, strlen(SMK_FSDEFAULT)) == 0) { op += strlen(SMK_FSDEFAULT); - nsp = smk_import(op, 0); - if (nsp != NULL) { - sp->smk_default = nsp; + skp = smk_import_entry(op, 0); + if (skp != NULL) { + sp->smk_default = skp; specified = 1; } } else if (strncmp(op, SMK_FSROOT, strlen(SMK_FSROOT)) == 0) { op += strlen(SMK_FSROOT); - nsp = smk_import(op, 0); - if (nsp != NULL) { - sp->smk_root = nsp; + skp = smk_import_entry(op, 0); + if (skp != NULL) { + sp->smk_root = skp; specified = 1; } } else if (strncmp(op, SMK_FSTRANS, strlen(SMK_FSTRANS)) == 0) { op += strlen(SMK_FSTRANS); - nsp = smk_import(op, 0); - if (nsp != NULL) { - sp->smk_root = nsp; + skp = smk_import_entry(op, 0); + if (skp != NULL) { + sp->smk_root = skp; transmute = 1; specified = 1; } @@ -612,8 +614,8 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) * Unprivileged mounts get root and default from the caller. */ skp = smk_of_current(); - sp->smk_root = skp->smk_known; - sp->smk_default = skp->smk_known; + sp->smk_root = skp; + sp->smk_default = skp; } /* * Initialize the root inode. @@ -690,7 +692,7 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) tracer = ptrace_parent(current); if (likely(tracer != NULL)) rc = smk_ptrace_rule_check(tracer, - isp->smk_task->smk_known, + isp->smk_task, PTRACE_MODE_ATTACH, __func__); rcu_read_unlock(); @@ -751,7 +753,7 @@ static int smack_inode_alloc_security(struct inode *inode) { struct smack_known *skp = smk_of_current(); - inode->i_security = new_inode_smack(skp->smk_known); + inode->i_security = new_inode_smack(skp); if (inode->i_security == NULL) return -ENOMEM; return 0; @@ -786,8 +788,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, { struct inode_smack *issp = inode->i_security; struct smack_known *skp = smk_of_current(); - char *isp = smk_of_inode(inode); - char *dsp = smk_of_inode(dir); + struct smack_known *isp = smk_of_inode(inode); + struct smack_known *dsp = smk_of_inode(dir); int may; if (name) @@ -795,7 +797,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, if (value) { rcu_read_lock(); - may = smk_access_entry(skp->smk_known, dsp, &skp->smk_rules); + may = smk_access_entry(skp->smk_known, dsp->smk_known, + &skp->smk_rules); rcu_read_unlock(); /* @@ -810,13 +813,13 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, issp->smk_flags |= SMK_INODE_CHANGED; } - *value = kstrdup(isp, GFP_NOFS); + *value = kstrdup(isp->smk_known, GFP_NOFS); if (*value == NULL) return -ENOMEM; } if (len) - *len = strlen(isp); + *len = strlen(isp->smk_known); return 0; } @@ -832,7 +835,7 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, static int smack_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { - char *isp; + struct smack_known *isp; struct smk_audit_info ad; int rc; @@ -939,7 +942,7 @@ static int smack_inode_rename(struct inode *old_inode, struct dentry *new_dentry) { int rc; - char *isp; + struct smack_known *isp; struct smk_audit_info ad; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); @@ -1127,9 +1130,9 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, if (strcmp(name, XATTR_NAME_SMACK) == 0) { skp = smk_import_entry(value, size); if (skp != NULL) - isp->smk_inode = skp->smk_known; + isp->smk_inode = skp; else - isp->smk_inode = smack_known_invalid.smk_known; + isp->smk_inode = &smack_known_invalid; } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) { skp = smk_import_entry(value, size); if (skp != NULL) @@ -1238,14 +1241,14 @@ static int smack_inode_getsecurity(const struct inode *inode, struct socket *sock; struct super_block *sbp; struct inode *ip = (struct inode *)inode; - char *isp; + struct smack_known *isp; int ilen; int rc = 0; if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) { isp = smk_of_inode(inode); - ilen = strlen(isp); - *buffer = isp; + ilen = strlen(isp->smk_known); + *buffer = isp->smk_known; return ilen; } @@ -1263,15 +1266,15 @@ static int smack_inode_getsecurity(const struct inode *inode, ssp = sock->sk->sk_security; if (strcmp(name, XATTR_SMACK_IPIN) == 0) - isp = ssp->smk_in->smk_known; + isp = ssp->smk_in; else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) - isp = ssp->smk_out->smk_known; + isp = ssp->smk_out; else return -EOPNOTSUPP; - ilen = strlen(isp); + ilen = strlen(isp->smk_known); if (rc == 0) { - *buffer = isp; + *buffer = isp->smk_known; rc = ilen; } @@ -1307,7 +1310,7 @@ static void smack_inode_getsecid(const struct inode *inode, u32 *secid) { struct inode_smack *isp = inode->i_security; - *secid = smack_to_secid(isp->smk_inode); + *secid = isp->smk_inode->smk_secid; } /* @@ -1346,7 +1349,7 @@ static int smack_file_alloc_security(struct file *file) { struct smack_known *skp = smk_of_current(); - file->f_security = skp->smk_known; + file->f_security = skp; return 0; } @@ -1474,7 +1477,7 @@ static int smack_mmap_file(struct file *file, struct smack_known *mkp; struct smack_rule *srp; struct task_smack *tsp; - char *osmack; + struct smack_known *okp; struct inode_smack *isp; int may; int mmay; @@ -1500,18 +1503,19 @@ static int smack_mmap_file(struct file *file, * to that rule's object label. */ list_for_each_entry_rcu(srp, &skp->smk_rules, list) { - osmack = srp->smk_object; + okp = srp->smk_object; /* * Matching labels always allows access. */ - if (mkp->smk_known == osmack) + if (mkp->smk_known == okp->smk_known) continue; /* * If there is a matching local rule take * that into account as well. */ - may = smk_access_entry(srp->smk_subject->smk_known, osmack, - &tsp->smk_rules); + may = smk_access_entry(srp->smk_subject->smk_known, + okp->smk_known, + &tsp->smk_rules); if (may == -ENOENT) may = srp->smk_access; else @@ -1528,8 +1532,8 @@ static int smack_mmap_file(struct file *file, * If there isn't one a SMACK64MMAP subject * can't have as much access as current. */ - mmay = smk_access_entry(mkp->smk_known, osmack, - &mkp->smk_rules); + mmay = smk_access_entry(mkp->smk_known, okp->smk_known, + &mkp->smk_rules); if (mmay == -ENOENT) { rc = -EACCES; break; @@ -1538,8 +1542,8 @@ static int smack_mmap_file(struct file *file, * If there is a local entry it modifies the * potential access, too. */ - tmay = smk_access_entry(mkp->smk_known, osmack, - &tsp->smk_rules); + tmay = smk_access_entry(mkp->smk_known, okp->smk_known, + &tsp->smk_rules); if (tmay != -ENOENT) mmay &= tmay; @@ -1570,7 +1574,7 @@ static int smack_file_set_fowner(struct file *file) { struct smack_known *skp = smk_of_current(); - file->f_security = skp->smk_known; + file->f_security = skp; return 0; } @@ -1600,15 +1604,15 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, file = container_of(fown, struct file, f_owner); /* we don't log here as rc can be overriden */ - skp = smk_find_entry(file->f_security); - rc = smk_access(skp, tkp->smk_known, MAY_WRITE, NULL); - rc = smk_bu_note("sigiotask", skp, tkp->smk_known, MAY_WRITE, rc); + skp = file->f_security; + rc = smk_access(skp, tkp, MAY_WRITE, NULL); + rc = smk_bu_note("sigiotask", skp, tkp, MAY_WRITE, rc); if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE)) rc = 0; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, tsk); - smack_log(file->f_security, tkp->smk_known, MAY_WRITE, rc, &ad); + smack_log(skp->smk_known, tkp->smk_known, MAY_WRITE, rc, &ad); return rc; } @@ -1805,7 +1809,7 @@ static int smack_kernel_create_files_as(struct cred *new, struct inode_smack *isp = inode->i_security; struct task_smack *tsp = new->security; - tsp->smk_forked = smk_find_entry(isp->smk_inode); + tsp->smk_forked = isp->smk_inode; tsp->smk_task = tsp->smk_forked; return 0; } @@ -1827,7 +1831,7 @@ static int smk_curacc_on_task(struct task_struct *p, int access, smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, p); - rc = smk_curacc(skp->smk_known, access, &ad); + rc = smk_curacc(skp, access, &ad); rc = smk_bu_task(p, access, rc); return rc; } @@ -1992,7 +1996,7 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, * can write the receiver. */ if (secid == 0) { - rc = smk_curacc(tkp->smk_known, MAY_WRITE, &ad); + rc = smk_curacc(tkp, MAY_WRITE, &ad); rc = smk_bu_task(p, MAY_WRITE, rc); return rc; } @@ -2002,8 +2006,8 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, * we can't take privilege into account. */ skp = smack_from_secid(secid); - rc = smk_access(skp, tkp->smk_known, MAY_WRITE, &ad); - rc = smk_bu_note("USB signal", skp, tkp->smk_known, MAY_WRITE, rc); + rc = smk_access(skp, tkp, MAY_WRITE, &ad); + rc = smk_bu_note("USB signal", skp, tkp, MAY_WRITE, rc); return rc; } @@ -2038,7 +2042,7 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode) struct inode_smack *isp = inode->i_security; struct smack_known *skp = smk_of_task(task_security(p)); - isp->smk_inode = skp->smk_known; + isp->smk_inode = skp; } /* @@ -2096,7 +2100,7 @@ static void smack_sk_free_security(struct sock *sk) * * Returns the label of the far end or NULL if it's not special. */ -static char *smack_host_label(struct sockaddr_in *sip) +static struct smack_known *smack_host_label(struct sockaddr_in *sip) { struct smk_netlbladdr *snp; struct in_addr *siap = &sip->sin_addr; @@ -2113,7 +2117,7 @@ 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)) { /* we have found the special CIPSO option */ - if (snp->smk_label == smack_cipso_option) + if (snp->smk_label == &smack_cipso_option) return NULL; return snp->smk_label; } @@ -2178,13 +2182,13 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) struct smack_known *skp; int rc; int sk_lbl; - char *hostsp; + struct smack_known *hkp; struct socket_smack *ssp = sk->sk_security; struct smk_audit_info ad; rcu_read_lock(); - hostsp = smack_host_label(sap); - if (hostsp != NULL) { + hkp = smack_host_label(sap); + if (hkp != NULL) { #ifdef CONFIG_AUDIT struct lsm_network_audit net; @@ -2195,8 +2199,8 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) #endif sk_lbl = SMACK_UNLABELED_SOCKET; skp = ssp->smk_out; - rc = smk_access(skp, hostsp, MAY_WRITE, &ad); - rc = smk_bu_note("IPv4 host check", skp, hostsp, MAY_WRITE, rc); + rc = smk_access(skp, hkp, MAY_WRITE, &ad); + rc = smk_bu_note("IPv4 host check", skp, hkp, MAY_WRITE, rc); } else { sk_lbl = SMACK_CIPSO_SOCKET; rc = 0; @@ -2297,7 +2301,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, struct socket_smack *ssp = sk->sk_security; struct smack_known *skp; unsigned short port = 0; - char *object; + struct smack_known *object; struct smk_audit_info ad; int rc; #ifdef CONFIG_AUDIT @@ -2306,10 +2310,10 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, if (act == SMK_RECEIVING) { skp = smack_net_ambient; - object = ssp->smk_in->smk_known; + object = ssp->smk_in; } else { skp = ssp->smk_out; - object = smack_net_ambient->smk_known; + object = smack_net_ambient; } /* @@ -2336,7 +2340,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, list_for_each_entry(spp, &smk_ipv6_port_list, list) { if (spp->smk_port != port) continue; - object = spp->smk_in->smk_known; + object = spp->smk_in; if (act == SMK_CONNECTING) ssp->smk_packet = spp->smk_out; break; @@ -2387,7 +2391,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, return -EINVAL; if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) { - nsp->smk_inode = skp->smk_known; + nsp->smk_inode = skp; nsp->smk_flags |= SMK_INODE_INSTANT; return 0; } @@ -2529,7 +2533,7 @@ static int smack_msg_msg_alloc_security(struct msg_msg *msg) { struct smack_known *skp = smk_of_current(); - msg->security = skp->smk_known; + msg->security = skp; return 0; } @@ -2550,9 +2554,9 @@ static void smack_msg_msg_free_security(struct msg_msg *msg) * * Returns a pointer to the smack value */ -static char *smack_of_shm(struct shmid_kernel *shp) +static struct smack_known *smack_of_shm(struct shmid_kernel *shp) { - return (char *)shp->shm_perm.security; + return (struct smack_known *)shp->shm_perm.security; } /** @@ -2566,7 +2570,7 @@ static int smack_shm_alloc_security(struct shmid_kernel *shp) struct kern_ipc_perm *isp = &shp->shm_perm; struct smack_known *skp = smk_of_current(); - isp->security = skp->smk_known; + isp->security = skp; return 0; } @@ -2592,7 +2596,7 @@ static void smack_shm_free_security(struct shmid_kernel *shp) */ static int smk_curacc_shm(struct shmid_kernel *shp, int access) { - char *ssp = smack_of_shm(shp); + struct smack_known *ssp = smack_of_shm(shp); struct smk_audit_info ad; int rc; @@ -2677,9 +2681,9 @@ static int smack_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, * * Returns a pointer to the smack value */ -static char *smack_of_sem(struct sem_array *sma) +static struct smack_known *smack_of_sem(struct sem_array *sma) { - return (char *)sma->sem_perm.security; + return (struct smack_known *)sma->sem_perm.security; } /** @@ -2693,7 +2697,7 @@ static int smack_sem_alloc_security(struct sem_array *sma) struct kern_ipc_perm *isp = &sma->sem_perm; struct smack_known *skp = smk_of_current(); - isp->security = skp->smk_known; + isp->security = skp; return 0; } @@ -2719,7 +2723,7 @@ static void smack_sem_free_security(struct sem_array *sma) */ static int smk_curacc_sem(struct sem_array *sma, int access) { - char *ssp = smack_of_sem(sma); + struct smack_known *ssp = smack_of_sem(sma); struct smk_audit_info ad; int rc; @@ -2815,7 +2819,7 @@ static int smack_msg_queue_alloc_security(struct msg_queue *msq) struct kern_ipc_perm *kisp = &msq->q_perm; struct smack_known *skp = smk_of_current(); - kisp->security = skp->smk_known; + kisp->security = skp; return 0; } @@ -2836,11 +2840,11 @@ static void smack_msg_queue_free_security(struct msg_queue *msq) * smack_of_msq - the smack pointer for the msq * @msq: the object * - * Returns a pointer to the smack value + * Returns a pointer to the smack label entry */ -static char *smack_of_msq(struct msg_queue *msq) +static struct smack_known *smack_of_msq(struct msg_queue *msq) { - return (char *)msq->q_perm.security; + return (struct smack_known *)msq->q_perm.security; } /** @@ -2852,7 +2856,7 @@ static char *smack_of_msq(struct msg_queue *msq) */ static int smk_curacc_msq(struct msg_queue *msq, int access) { - char *msp = smack_of_msq(msq); + struct smack_known *msp = smack_of_msq(msq); struct smk_audit_info ad; int rc; @@ -2955,7 +2959,7 @@ static int smack_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, */ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) { - char *isp = ipp->security; + struct smack_known *iskp = ipp->security; int may = smack_flags_to_may(flag); struct smk_audit_info ad; int rc; @@ -2964,8 +2968,8 @@ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); ad.a.u.ipc_id = ipp->id; #endif - rc = smk_curacc(isp, may, &ad); - rc = smk_bu_current("svipc", isp, may, rc); + rc = smk_curacc(iskp, may, &ad); + rc = smk_bu_current("svipc", iskp, may, rc); return rc; } @@ -2976,9 +2980,9 @@ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) */ static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid) { - char *smack = ipp->security; + struct smack_known *iskp = ipp->security; - *secid = smack_to_secid(smack); + *secid = iskp->smk_secid; } /** @@ -2995,7 +2999,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) struct inode_smack *isp; struct smack_known *skp; struct smack_known *ckp = smk_of_current(); - char *final; + struct smack_known *final; char trattr[TRANS_TRUE_SIZE]; int transflag = 0; int rc; @@ -3035,8 +3039,8 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * so there's no opportunity to set the mount * options. */ - sbsp->smk_root = smack_known_star.smk_known; - sbsp->smk_default = smack_known_star.smk_known; + sbsp->smk_root = &smack_known_star; + sbsp->smk_default = &smack_known_star; } isp->smk_inode = sbsp->smk_root; isp->smk_flags |= SMK_INODE_INSTANT; @@ -3066,7 +3070,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * * Cgroupfs is special */ - final = smack_known_star.smk_known; + final = &smack_known_star; break; case DEVPTS_SUPER_MAGIC: /* @@ -3074,7 +3078,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * Programs that change smack have to treat the * pty with respect. */ - final = ckp->smk_known; + final = ckp; break; case PROC_SUPER_MAGIC: /* @@ -3088,7 +3092,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * but watch out, because they're volitile, * getting recreated on every reboot. */ - final = smack_known_star.smk_known; + final = &smack_known_star; /* * No break. * @@ -3107,7 +3111,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * UNIX domain sockets use lower level socket data. */ if (S_ISSOCK(inode->i_mode)) { - final = smack_known_star.smk_known; + final = &smack_known_star; break; } /* @@ -3124,7 +3128,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) dp = dget(opt_dentry); skp = smk_fetch(XATTR_NAME_SMACK, inode, dp); if (skp != NULL) - final = skp->smk_known; + final = skp; /* * Transmuting directory @@ -3173,7 +3177,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) } if (final == NULL) - isp->smk_inode = ckp->smk_known; + isp->smk_inode = ckp; else isp->smk_inode = final; @@ -3298,12 +3302,11 @@ static int smack_unix_stream_connect(struct sock *sock, smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); smk_ad_setfield_u_net_sk(&ad, other); #endif - rc = smk_access(skp, okp->smk_known, MAY_WRITE, &ad); - rc = smk_bu_note("UDS connect", skp, okp->smk_known, - MAY_WRITE, rc); + rc = smk_access(skp, okp, MAY_WRITE, &ad); + rc = smk_bu_note("UDS connect", skp, okp, MAY_WRITE, rc); if (rc == 0) { - rc = smk_access(okp, skp->smk_known, MAY_WRITE, NULL); - rc = smk_bu_note("UDS connect", okp, skp->smk_known, + rc = smk_access(okp, skp, MAY_WRITE, NULL); + rc = smk_bu_note("UDS connect", okp, skp, MAY_WRITE, rc); } } @@ -3331,7 +3334,6 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) { struct socket_smack *ssp = sock->sk->sk_security; struct socket_smack *osp = other->sk->sk_security; - struct smack_known *skp; struct smk_audit_info ad; int rc; @@ -3345,10 +3347,8 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) if (smack_privileged(CAP_MAC_OVERRIDE)) return 0; - skp = ssp->smk_out; - rc = smk_access(skp, osp->smk_in->smk_known, MAY_WRITE, &ad); - rc = smk_bu_note("UDS send", skp, osp->smk_in->smk_known, - MAY_WRITE, rc); + rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad); + rc = smk_bu_note("UDS send", ssp->smk_out, osp->smk_in, MAY_WRITE, rc); return rc; } @@ -3563,8 +3563,8 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) * This is the simplist possible security model * for networking. */ - rc = smk_access(skp, ssp->smk_in->smk_known, MAY_WRITE, &ad); - rc = smk_bu_note("IPv4 delivery", skp, ssp->smk_in->smk_known, + rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); + rc = smk_bu_note("IPv4 delivery", skp, ssp->smk_in, MAY_WRITE, rc); if (rc != 0) netlbl_skbuff_err(skb, rc, 0); @@ -3708,7 +3708,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct netlbl_lsm_secattr secattr; struct sockaddr_in addr; struct iphdr *hdr; - char *hsp; + struct smack_known *hskp; int rc; struct smk_audit_info ad; #ifdef CONFIG_AUDIT @@ -3745,9 +3745,8 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, * Receiving a packet requires that the other end be able to write * here. Read access is not required. */ - rc = smk_access(skp, ssp->smk_in->smk_known, MAY_WRITE, &ad); - rc = smk_bu_note("IPv4 connect", skp, ssp->smk_in->smk_known, - MAY_WRITE, rc); + rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); + rc = smk_bu_note("IPv4 connect", skp, ssp->smk_in, MAY_WRITE, rc); if (rc != 0) return rc; @@ -3765,10 +3764,10 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, hdr = ip_hdr(skb); addr.sin_addr.s_addr = hdr->saddr; rcu_read_lock(); - hsp = smack_host_label(&addr); + hskp = smack_host_label(&addr); rcu_read_unlock(); - if (hsp == NULL) + if (hskp == NULL) rc = netlbl_req_setattr(req, &skp->smk_netlabel); else netlbl_req_delattr(req); @@ -3820,7 +3819,7 @@ static int smack_key_alloc(struct key *key, const struct cred *cred, { struct smack_known *skp = smk_of_task(cred->security); - key->security = skp->smk_known; + key->security = skp; return 0; } @@ -3909,6 +3908,7 @@ static int smack_key_permission(key_ref_t key_ref, */ static int smack_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) { + struct smack_known *skp; char **rule = (char **)vrule; *rule = NULL; @@ -3918,7 +3918,9 @@ static int smack_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) if (op != Audit_equal && op != Audit_not_equal) return -EINVAL; - *rule = smk_import(rulestr, 0); + skp = smk_import_entry(rulestr, 0); + if (skp) + *rule = skp->smk_known; return 0; } @@ -4037,7 +4039,12 @@ static int smack_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) */ static int smack_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid) { - *secid = smack_to_secid(secdata); + struct smack_known *skp = smk_find_entry(secdata); + + if (skp) + *secid = skp->smk_secid; + else + *secid = 0; return 0; } diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 49a2248b525c..bce4e8f1b267 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -131,14 +131,17 @@ LIST_HEAD(smack_rule_list); struct smack_parsed_rule { struct smack_known *smk_subject; - char *smk_object; + struct smack_known *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; +struct smack_known smack_cipso_option = { + .smk_known = SMACK_CIPSO_OPTION, + .smk_secid = 0, +}; /* * Values for parsing cipso rules @@ -339,7 +342,7 @@ static int smk_fill_rule(const char *subject, const char *object, if (rule->smk_subject == NULL) return -EINVAL; - rule->smk_object = smk_import(object, len); + rule->smk_object = smk_import_entry(object, len); if (rule->smk_object == NULL) return -EINVAL; } else { @@ -359,7 +362,7 @@ static int smk_fill_rule(const char *subject, const char *object, kfree(cp); if (skp == NULL) return -ENOENT; - rule->smk_object = skp->smk_known; + rule->smk_object = skp; } rule->smk_access1 = smk_perm_from_str(access1); @@ -598,13 +601,15 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) * anything you read back. */ if (strlen(srp->smk_subject->smk_known) >= max || - strlen(srp->smk_object) >= max) + strlen(srp->smk_object->smk_known) >= max) return; if (srp->smk_access == 0) return; - seq_printf(s, "%s %s", srp->smk_subject->smk_known, srp->smk_object); + seq_printf(s, "%s %s", + srp->smk_subject->smk_known, + srp->smk_object->smk_known); seq_putc(s, ' '); @@ -1073,7 +1078,7 @@ static int netlbladdr_seq_show(struct seq_file *s, void *v) for (maskn = 0; temp_mask; temp_mask <<= 1, maskn++); seq_printf(s, "%u.%u.%u.%u/%d %s\n", - hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label); + hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label->smk_known); return 0; } @@ -1153,10 +1158,10 @@ static void smk_netlbladdr_insert(struct smk_netlbladdr *new) static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct smk_netlbladdr *skp; + struct smk_netlbladdr *snp; struct sockaddr_in newname; char *smack; - char *sp; + struct smack_known *skp; char *data; char *host = (char *)&newname.sin_addr.s_addr; int rc; @@ -1219,15 +1224,15 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, * If smack begins with '-', it is an option, don't import it */ if (smack[0] != '-') { - sp = smk_import(smack, 0); - if (sp == NULL) { + skp = smk_import_entry(smack, 0); + if (skp == NULL) { rc = -EINVAL; goto free_out; } } else { /* check known options */ - if (strcmp(smack, smack_cipso_option) == 0) - sp = (char *)smack_cipso_option; + if (strcmp(smack, smack_cipso_option.smk_known) == 0) + skp = &smack_cipso_option; else { rc = -EINVAL; goto free_out; @@ -1250,9 +1255,9 @@ 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 */ 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) { + list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list) { + if (snp->smk_host.sin_addr.s_addr == nsa && + snp->smk_mask.s_addr == mask.s_addr) { found = 1; break; } @@ -1260,26 +1265,26 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, smk_netlabel_audit_set(&audit_info); if (found == 0) { - skp = kzalloc(sizeof(*skp), GFP_KERNEL); - if (skp == NULL) + snp = kzalloc(sizeof(*snp), GFP_KERNEL); + if (snp == NULL) rc = -ENOMEM; else { rc = 0; - skp->smk_host.sin_addr.s_addr = newname.sin_addr.s_addr; - skp->smk_mask.s_addr = mask.s_addr; - skp->smk_label = sp; - smk_netlbladdr_insert(skp); + snp->smk_host.sin_addr.s_addr = newname.sin_addr.s_addr; + snp->smk_mask.s_addr = mask.s_addr; + snp->smk_label = skp; + smk_netlbladdr_insert(snp); } } else { /* we delete the unlabeled entry, only if the previous label * wasn't the special CIPSO option */ - if (skp->smk_label != smack_cipso_option) + if (snp->smk_label != &smack_cipso_option) rc = netlbl_cfg_unlbl_static_del(&init_net, NULL, - &skp->smk_host.sin_addr, &skp->smk_mask, + &snp->smk_host.sin_addr, &snp->smk_mask, PF_INET, &audit_info); else rc = 0; - skp->smk_label = sp; + snp->smk_label = skp; } /* @@ -1287,10 +1292,10 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, * this host so that incoming packets get labeled. * but only if we didn't get the special CIPSO option */ - if (rc == 0 && sp != smack_cipso_option) + if (rc == 0 && skp != &smack_cipso_option) rc = netlbl_cfg_unlbl_static_add(&init_net, NULL, - &skp->smk_host.sin_addr, &skp->smk_mask, PF_INET, - smack_to_secid(skp->smk_label), &audit_info); + &snp->smk_host.sin_addr, &snp->smk_mask, PF_INET, + snp->smk_label->smk_secid, &audit_info); if (rc == 0) rc = count; -- cgit v1.2.3 From fbff66108352d19b5cffa7dce26d7638c9dd4d70 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Thu, 28 Aug 2014 04:43:09 -0700 Subject: security: Silence shadow warning Renaming an unused formal parameter in the static inline function security_inode_init_security eliminates many W=2 warnings. Signed-off-by: Mark Rustad Signed-off-by: Jeff Kirsher Signed-off-by: James Morris --- include/linux/security.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/security.h b/include/linux/security.h index 623f90e5f38d..3b3aeb1b74cb 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -2108,7 +2108,7 @@ static inline int security_dentry_init_security(struct dentry *dentry, static inline int security_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, - const initxattrs initxattrs, + const initxattrs xattrs, void *fs_data) { return 0; -- cgit v1.2.3 From 9a8d289fbcb7dfd1fc74959e9930b406e76b2002 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 28 Jul 2014 07:59:49 -0400 Subject: ima: fix ima_alloc_atfm() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The patch 3bcced39ea7d: "ima: use ahash API for file hash calculation" from Feb 26, 2014, leads to the following static checker warning: security/integrity/ima/ima_crypto.c:204 ima_alloc_atfm()          error: buffer overflow 'hash_algo_name' 17 <= 17 Unlike shash tfm memory, which is allocated on initialization, the ahash tfm memory allocation is deferred until needed. This patch fixes the case where ima_ahash_tfm has not yet been allocated and the file's signature/hash xattr contains an invalid hash algorithm. Although we can not verify the xattr, we still need to measure the file. Use the default IMA hash algorithm. Changelog: - set valid algo before testing tfm - based on Dmitry's comment Reported-by: Dan Carpenter Signed-off-by: Mimi Zohar Signed-off-by: Dmitry Kasatkin --- security/integrity/ima/ima_crypto.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 0bd732843fe7..2d103dc13d90 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -200,7 +200,10 @@ static struct crypto_ahash *ima_alloc_atfm(enum hash_algo algo) struct crypto_ahash *tfm = ima_ahash_tfm; int rc; - if ((algo != ima_hash_algo && algo < HASH_ALGO__LAST) || !tfm) { + if (algo < 0 || algo >= HASH_ALGO__LAST) + algo = ima_hash_algo; + + if (algo != ima_hash_algo || !tfm) { tfm = crypto_alloc_ahash(hash_algo_name[algo], 0, 0); if (!IS_ERR(tfm)) { if (algo == ima_hash_algo) -- cgit v1.2.3 From 23c19e2ca736722a9523b64b07cda7efab7b6c57 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 15 Aug 2014 13:28:52 +0300 Subject: ima: prevent buffer overflow in ima_alloc_tfm() This patch fixes the case where the file's signature/hash xattr contains an invalid hash algorithm. Although we can not verify the xattr, we still need to measure the file. Use the default IMA hash algorithm. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_crypto.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 2d103dc13d90..1178b307d562 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -116,7 +116,10 @@ static struct crypto_shash *ima_alloc_tfm(enum hash_algo algo) struct crypto_shash *tfm = ima_shash_tfm; int rc; - if (algo != ima_hash_algo && algo < HASH_ALGO__LAST) { + if (algo < 0 || algo >= HASH_ALGO__LAST) + algo = ima_hash_algo; + + if (algo != ima_hash_algo) { tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0); if (IS_ERR(tfm)) { rc = PTR_ERR(tfm); -- cgit v1.2.3 From 27cd1fc3ae5374a4a86662c67033f15ef27b2461 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 23 Jun 2014 20:32:56 +0300 Subject: ima: fix fallback to use new_sync_read() 3.16 commit aad4f8bb42af06371aa0e85bf0cd9d52c0494985 'switch simple generic_file_aio_read() users to ->read_iter()' replaced ->aio_read with ->read_iter in most of the file systems and introduced new_sync_read() as a replacement for do_sync_read(). Most of file systems set '->read' and ima_kernel_read is not affected. When ->read is not set, this patch adopts fallback call changes from the vfs_read. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar Cc: 3.16+ --- security/integrity/ima/ima_crypto.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 1178b307d562..3b26472fbf0a 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -80,19 +80,19 @@ static int ima_kernel_read(struct file *file, loff_t offset, { mm_segment_t old_fs; char __user *buf = addr; - ssize_t ret; + ssize_t ret = -EINVAL; if (!(file->f_mode & FMODE_READ)) return -EBADF; - if (!file->f_op->read && !file->f_op->aio_read) - return -EINVAL; old_fs = get_fs(); set_fs(get_ds()); if (file->f_op->read) ret = file->f_op->read(file, buf, count, &offset); - else + else if (file->f_op->aio_read) ret = do_sync_read(file, buf, count, &offset); + else if (file->f_op->read_iter) + ret = new_sync_read(file, buf, count, &offset); set_fs(old_fs); return ret; } -- cgit v1.2.3 From e7d021e28328e0cc47b21cb9c6d8885326b0c2f5 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 15 Aug 2014 14:09:19 +0300 Subject: evm: fix checkpatch warnings This patch fixes checkpatch 'return' warnings introduced with commit 9819cf2 "checkpatch: warn on unnecessary void function return statements". Use scripts/checkpatch.pl --file security/integrity/evm/evm_main.c to produce the warnings. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/evm/evm_main.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 3bcb80df4d01..fb71f55295dc 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -352,7 +352,6 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, return; evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); - return; } /** @@ -372,7 +371,6 @@ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) mutex_lock(&inode->i_mutex); evm_update_evmxattr(dentry, xattr_name, NULL, 0); mutex_unlock(&inode->i_mutex); - return; } /** @@ -414,7 +412,6 @@ void evm_inode_post_setattr(struct dentry *dentry, int ia_valid) if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) evm_update_evmxattr(dentry, NULL, NULL, 0); - return; } /* -- cgit v1.2.3 From c3ce6dfa48e3879206382cdfdc015bffc50dce30 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 2 Sep 2014 13:52:15 +0100 Subject: KEYS: Set pr_fmt() in asymmetric key signature handling Printing in base signature handling should have a prefix, so set pr_fmt(). Signed-off-by: David Howells Signed-off-by: James Morris --- crypto/asymmetric_keys/signature.c | 1 + 1 file changed, 1 insertion(+) diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c index 50b3f880b4ff..7525fd183574 100644 --- a/crypto/asymmetric_keys/signature.c +++ b/crypto/asymmetric_keys/signature.c @@ -11,6 +11,7 @@ * 2 of the Licence, or (at your option) any later version. */ +#define pr_fmt(fmt) "SIG: "fmt #include #include #include -- cgit v1.2.3 From a7a91a1928fe69cc98814cb746d5171ae14d757e Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 3 Sep 2014 10:51:59 -0400 Subject: selinux: fix a problem with IPv6 traffic denials in selinux_ip_postroute() A previous commit c0828e50485932b7e019df377a6b0a8d1ebd3080 ("selinux: process labeled IPsec TCP SYN-ACK packets properly in selinux_ip_postroute()") mistakenly left out a 'break' from a switch statement which caused problems with IPv6 traffic. Thanks to Florian Westphal for reporting and debugging the issue. Reported-by: Florian Westphal Signed-off-by: Paul Moore --- security/selinux/hooks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 6c90d491fab4..e1e082796a49 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4993,6 +4993,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, case PF_INET6: if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) return NF_ACCEPT; + break; default: return NF_DROP_ERR(-ECONNREFUSED); } -- cgit v1.2.3 From a0cfd75fdc46b56978ece383a7d6f6b04e9087ad Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 12 Aug 2014 15:41:17 -0700 Subject: seccomp: Add reviewers to MAINTAINERS This adds two reviewers to the seccomp tree. Signed-off-by: Kees Cook --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 55762cba8516..992335ca0b2c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7955,6 +7955,8 @@ F: drivers/mmc/host/sdhci-pltfm.[ch] SECURE COMPUTING M: Kees Cook +R: Andy Lutomirski +R: Will Drewry T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git seccomp S: Supported F: kernel/seccomp.c -- cgit v1.2.3 From 1f1009791b2e81f106d4809007720495ba3ed90c Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 15 Aug 2014 13:49:22 +0300 Subject: evm: prevent passing integrity check if xattr read fails This patch fixes a bug, where evm_verify_hmac() returns INTEGRITY_PASS if inode->i_op->getxattr() returns an error in evm_find_protected_xattrs. Signed-off-by: Dmitry Kasatkin --- security/integrity/evm/evm_main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index fb71f55295dc..40220124364c 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -126,14 +126,15 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0, GFP_NOFS); if (rc <= 0) { - if (rc == 0) - evm_status = INTEGRITY_FAIL; /* empty */ - else if (rc == -ENODATA) { + evm_status = INTEGRITY_FAIL; + if (rc == -ENODATA) { rc = evm_find_protected_xattrs(dentry); if (rc > 0) evm_status = INTEGRITY_NOLABEL; else if (rc == 0) evm_status = INTEGRITY_NOXATTRS; /* new file */ + } else if (rc == -EOPNOTSUPP) { + evm_status = INTEGRITY_UNKNOWN; } goto out; } -- cgit v1.2.3 From b151d6b00bbb798c58f2f21305e7d43fa763f34f Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 27 Jun 2014 18:04:27 +0300 Subject: ima: provide flag to identify new empty files On ima_file_free(), newly created empty files are not labeled with an initial security.ima value, because the iversion did not change. Commit dff6efc "fs: fix iversion handling" introduced a change in iversion behavior. To verify this change use the shell command: $ (exec >foo) $ getfattr -h -e hex -d -m security foo This patch defines the IMA_NEW_FILE flag. The flag is initially set, when IMA detects that a new file is created, and subsequently checked on the ima_file_free() hook to set the initial security.ima value. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar Cc: 3.14+ --- security/integrity/ima/ima_appraise.c | 7 +++++-- security/integrity/ima/ima_main.c | 12 +++++++----- security/integrity/integrity.h | 1 + 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 86bfd5c5df85..a4605d677248 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -202,8 +202,11 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, goto out; cause = "missing-hash"; - status = - (inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL; + status = INTEGRITY_NOLABEL; + if (inode->i_size == 0) { + iint->flags |= IMA_NEW_FILE; + status = INTEGRITY_PASS; + } goto out; } diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 2917f980bf30..0a2298f90c9c 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -124,11 +124,13 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint, return; mutex_lock(&inode->i_mutex); - if (atomic_read(&inode->i_writecount) == 1 && - iint->version != inode->i_version) { - iint->flags &= ~IMA_DONE_MASK; - if (iint->flags & IMA_APPRAISE) - ima_update_xattr(iint, file); + if (atomic_read(&inode->i_writecount) == 1) { + if ((iint->version != inode->i_version) || + (iint->flags & IMA_NEW_FILE)) { + iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE); + if (iint->flags & IMA_APPRAISE) + ima_update_xattr(iint, file); + } } mutex_unlock(&inode->i_mutex); } diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 19b8e314ca96..904e68abd49e 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -31,6 +31,7 @@ #define IMA_DIGSIG 0x01000000 #define IMA_DIGSIG_REQUIRED 0x02000000 #define IMA_PERMIT_DIRECTIO 0x04000000 +#define IMA_NEW_FILE 0x08000000 #define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \ IMA_APPRAISE_SUBMASK) -- cgit v1.2.3 From 25db6bea1ff5a78ef493eefdcbb9c1d27134e560 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 3 Sep 2014 17:42:13 +0200 Subject: selinux: register nf hooks with single nf_register_hooks call Push ipv4 and ipv6 nf hooks into single array and register/unregister them via single call. Signed-off-by: Jiri Pirko Signed-off-by: Paul Moore --- security/selinux/hooks.c | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e1e082796a49..50978d3183ea 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6071,7 +6071,7 @@ security_initcall(selinux_init); #if defined(CONFIG_NETFILTER) -static struct nf_hook_ops selinux_ipv4_ops[] = { +static struct nf_hook_ops selinux_nf_ops[] = { { .hook = selinux_ipv4_postroute, .owner = THIS_MODULE, @@ -6092,12 +6092,8 @@ static struct nf_hook_ops selinux_ipv4_ops[] = { .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_SELINUX_FIRST, - } -}; - + }, #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - -static struct nf_hook_ops selinux_ipv6_ops[] = { { .hook = selinux_ipv6_postroute, .owner = THIS_MODULE, @@ -6111,32 +6107,24 @@ static struct nf_hook_ops selinux_ipv6_ops[] = { .pf = NFPROTO_IPV6, .hooknum = NF_INET_FORWARD, .priority = NF_IP6_PRI_SELINUX_FIRST, - } -}; - + }, #endif /* IPV6 */ +}; static int __init selinux_nf_ip_init(void) { - int err = 0; + int err; if (!selinux_enabled) - goto out; + return 0; printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n"); - err = nf_register_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops)); - if (err) - panic("SELinux: nf_register_hooks for IPv4: error %d\n", err); - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - err = nf_register_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops)); + err = nf_register_hooks(selinux_nf_ops, ARRAY_SIZE(selinux_nf_ops)); if (err) - panic("SELinux: nf_register_hooks for IPv6: error %d\n", err); -#endif /* IPV6 */ + panic("SELinux: nf_register_hooks: error %d\n", err); -out: - return err; + return 0; } __initcall(selinux_nf_ip_init); @@ -6146,10 +6134,7 @@ static void selinux_nf_ip_exit(void) { printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n"); - nf_unregister_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops)); -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - nf_unregister_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops)); -#endif /* IPV6 */ + nf_unregister_hooks(selinux_nf_ops, ARRAY_SIZE(selinux_nf_ops)); } #endif -- cgit v1.2.3 From 3dcbad52cf18c3c379e96b992d22815439ebbe53 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 2 Sep 2014 16:31:43 +0300 Subject: evm: properly handle INTEGRITY_NOXATTRS EVM status Unless an LSM labels a file during d_instantiate(), newly created files are not labeled with an initial security.evm xattr, until the file closes. EVM, before allowing a protected, security xattr to be written, verifies the existing 'security.evm' value is good. For newly created files without a security.evm label, this verification prevents writing any protected, security xattrs, until the file closes. Following is the example when this happens: fd = open("foo", O_CREAT | O_WRONLY, 0644); setxattr("foo", "security.SMACK64", value, sizeof(value), 0); close(fd); While INTEGRITY_NOXATTRS status is handled in other places, such as evm_inode_setattr(), it does not handle it in all cases in evm_protect_xattr(). By limiting the use of INTEGRITY_NOXATTRS to newly created files, we can now allow setting "protected" xattrs. Changelog: - limit the use of INTEGRITY_NOXATTRS to IMA identified new files Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar Cc: 3.14+ --- security/integrity/evm/evm_main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 40220124364c..9685af330de5 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -285,6 +285,13 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, goto out; } evm_status = evm_verify_current_integrity(dentry); + if (evm_status == INTEGRITY_NOXATTRS) { + struct integrity_iint_cache *iint; + + iint = integrity_iint_find(dentry->d_inode); + if (iint && (iint->flags & IMA_NEW_FILE)) + return 0; + } out: if (evm_status != INTEGRITY_PASS) integrity_audit_msg(AUDIT_INTEGRITY_METADATA, dentry->d_inode, -- cgit v1.2.3 From 3034a146820c26fe6da66a45f6340fe87fe0983a Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 27 Jun 2014 18:15:44 +0300 Subject: ima: pass 'opened' flag to identify newly created files Empty files and missing xattrs do not guarantee that a file was just created. This patch passes FILE_CREATED flag to IMA to reliably identify new files. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar Cc: 3.14+ --- fs/namei.c | 2 +- fs/nfsd/vfs.c | 2 +- include/linux/ima.h | 4 ++-- security/integrity/ima/ima.h | 4 ++-- security/integrity/ima/ima_appraise.c | 4 ++-- security/integrity/ima/ima_main.c | 16 ++++++++-------- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 985c6f368485..005771f97189 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3058,7 +3058,7 @@ opened: error = open_check_o_direct(file); if (error) goto exit_fput; - error = ima_file_check(file, op->acc_mode); + error = ima_file_check(file, op->acc_mode, *opened); if (error) goto exit_fput; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 140c496f612c..d49c778faecb 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -709,7 +709,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, host_err = PTR_ERR(*filp); *filp = NULL; } else { - host_err = ima_file_check(*filp, may_flags); + host_err = ima_file_check(*filp, may_flags, 0); if (may_flags & NFSD_MAY_64BIT_COOKIE) (*filp)->f_mode |= FMODE_64BITHASH; diff --git a/include/linux/ima.h b/include/linux/ima.h index 7cf5e9b32550..120ccc53fcb7 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -15,7 +15,7 @@ struct linux_binprm; #ifdef CONFIG_IMA extern int ima_bprm_check(struct linux_binprm *bprm); -extern int ima_file_check(struct file *file, int mask); +extern int ima_file_check(struct file *file, int mask, int opened); extern void ima_file_free(struct file *file); extern int ima_file_mmap(struct file *file, unsigned long prot); extern int ima_module_check(struct file *file); @@ -27,7 +27,7 @@ static inline int ima_bprm_check(struct linux_binprm *bprm) return 0; } -static inline int ima_file_check(struct file *file, int mask) +static inline int ima_file_check(struct file *file, int mask, int opened) { return 0; } diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 57da4bd7ba0c..0fb456c20eda 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -177,7 +177,7 @@ void ima_delete_rules(void); int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, - int xattr_len); + int xattr_len, int opened); int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func); void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file); enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, @@ -193,7 +193,7 @@ static inline int ima_appraise_measurement(int func, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, - int xattr_len) + int xattr_len, int opened) { return INTEGRITY_UNKNOWN; } diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index a4605d677248..225fd944a4ef 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -183,7 +183,7 @@ int ima_read_xattr(struct dentry *dentry, int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, - int xattr_len) + int xattr_len, int opened) { static const char op[] = "appraise_data"; char *cause = "unknown"; @@ -203,7 +203,7 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, cause = "missing-hash"; status = INTEGRITY_NOLABEL; - if (inode->i_size == 0) { + if (opened & FILE_CREATED) { iint->flags |= IMA_NEW_FILE; status = INTEGRITY_PASS; } diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 0a2298f90c9c..f82cf9b8e92b 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -157,7 +157,7 @@ void ima_file_free(struct file *file) } static int process_measurement(struct file *file, const char *filename, - int mask, int function) + int mask, int function, int opened) { struct inode *inode = file_inode(file); struct integrity_iint_cache *iint; @@ -226,7 +226,7 @@ static int process_measurement(struct file *file, const char *filename, xattr_value, xattr_len); if (action & IMA_APPRAISE_SUBMASK) rc = ima_appraise_measurement(_func, iint, file, pathname, - xattr_value, xattr_len); + xattr_value, xattr_len, opened); if (action & IMA_AUDIT) ima_audit_measurement(iint, pathname); kfree(pathbuf); @@ -255,7 +255,7 @@ out: int ima_file_mmap(struct file *file, unsigned long prot) { if (file && (prot & PROT_EXEC)) - return process_measurement(file, NULL, MAY_EXEC, MMAP_CHECK); + return process_measurement(file, NULL, MAY_EXEC, MMAP_CHECK, 0); return 0; } @@ -277,7 +277,7 @@ int ima_bprm_check(struct linux_binprm *bprm) return process_measurement(bprm->file, (strcmp(bprm->filename, bprm->interp) == 0) ? bprm->filename : bprm->interp, - MAY_EXEC, BPRM_CHECK); + MAY_EXEC, BPRM_CHECK, 0); } /** @@ -290,12 +290,12 @@ int ima_bprm_check(struct linux_binprm *bprm) * On success return 0. On integrity appraisal error, assuming the file * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. */ -int ima_file_check(struct file *file, int mask) +int ima_file_check(struct file *file, int mask, int opened) { ima_rdwr_violation_check(file); return process_measurement(file, NULL, mask & (MAY_READ | MAY_WRITE | MAY_EXEC), - FILE_CHECK); + FILE_CHECK, opened); } EXPORT_SYMBOL_GPL(ima_file_check); @@ -318,7 +318,7 @@ int ima_module_check(struct file *file) #endif return 0; /* We rely on module signature checking */ } - return process_measurement(file, NULL, MAY_EXEC, MODULE_CHECK); + return process_measurement(file, NULL, MAY_EXEC, MODULE_CHECK, 0); } int ima_fw_from_file(struct file *file, char *buf, size_t size) @@ -329,7 +329,7 @@ int ima_fw_from_file(struct file *file, char *buf, size_t size) return -EACCES; /* INTEGRITY_UNKNOWN */ return 0; } - return process_measurement(file, NULL, MAY_EXEC, FIRMWARE_CHECK); + return process_measurement(file, NULL, MAY_EXEC, FIRMWARE_CHECK, 0); } static int __init init_ima(void) -- cgit v1.2.3 From d9a2e5d788d39f7593e2af5e1a365e2b9300679f Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 2 Jul 2014 15:12:26 +0300 Subject: integrity: prevent flooding with 'Request for unknown key' If file has IMA signature, IMA in enforce mode, but key is missing then file access is blocked and single error message is printed. If IMA appraisal is enabled in fix mode, then system runs as usual but might produce tons of 'Request for unknown key' messages. This patch switches 'pr_warn' to 'pr_err_ratelimited'. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/digsig_asymmetric.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 9eae4809006b..37e0d98517a8 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -13,6 +13,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -45,8 +46,8 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) } if (IS_ERR(key)) { - pr_warn("Request for unknown key '%s' err %ld\n", - name, PTR_ERR(key)); + pr_err_ratelimited("Request for unknown key '%s' err %ld\n", + name, PTR_ERR(key)); switch (PTR_ERR(key)) { /* Hide some search errors */ case -EACCES: -- cgit v1.2.3 From 65d98f3be25f7ee96af655f97e153d3d8d3d8ef9 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 19 Aug 2014 14:56:18 +0300 Subject: integrity: remove declaration of non-existing functions Commit f381c27 "integrity: move ima inode integrity data management" (re)moved few functions but left their declarations in header files. This patch removes them and also removes duplicated declaration of integrity_iint_find(). Commit c7de7ad "ima: remove unused cleanup functions". This patch removes these definitions as well. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 9 --------- security/integrity/integrity.h | 1 - 2 files changed, 10 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 0fb456c20eda..c6990a723f73 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -90,10 +90,7 @@ extern struct list_head ima_measurements; /* list of all measurements */ /* Internal IMA function definitions */ int ima_init(void); -void ima_cleanup(void); int ima_fs_init(void); -void ima_fs_cleanup(void); -int ima_inode_alloc(struct inode *inode); int ima_add_template_entry(struct ima_template_entry *entry, int violation, const char *op, struct inode *inode, const unsigned char *filename); @@ -151,12 +148,6 @@ int ima_store_template(struct ima_template_entry *entry, int violation, void ima_free_template_entry(struct ima_template_entry *entry); const char *ima_d_path(struct path *path, char **pathbuf); -/* rbtree tree calls to lookup, insert, delete - * integrity data associated with an inode. - */ -struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); -struct integrity_iint_cache *integrity_iint_find(struct inode *inode); - /* IMA policy related functions */ enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, FIRMWARE_CHECK, POST_SETATTR }; diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 904e68abd49e..c0379d13dbe1 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -117,7 +117,6 @@ struct integrity_iint_cache { /* rbtree tree calls to lookup, insert, delete * integrity data associated with an inode. */ -struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); struct integrity_iint_cache *integrity_iint_find(struct inode *inode); #define INTEGRITY_KEYRING_EVM 0 -- cgit v1.2.3 From f68c05f4d2d4e19c40f4ac1e769cc0a2f9f544a0 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 22 Aug 2014 09:43:55 +0300 Subject: ima: simplify conditional statement to improve performance Precede bit testing before string comparison makes code faster. Also refactor statement as a single line pointer assignment. Logic is following: we set 'xattr_ptr' to read xattr value when we will do appraisal or in any case when measurement template is other than 'ima'. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_main.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index f82cf9b8e92b..f7b85bf0eaf3 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -206,10 +206,8 @@ static int process_measurement(struct file *file, const char *filename, } template_desc = ima_template_desc_current(); - if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) { - if (action & IMA_APPRAISE_SUBMASK) - xattr_ptr = &xattr_value; - } else + if ((action & IMA_APPRAISE_SUBMASK) || + strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0) xattr_ptr = &xattr_value; rc = ima_collect_measurement(iint, file, xattr_ptr, &xattr_len); -- cgit v1.2.3 From 3a8a2eadc4946ce3af39b3447c32532324538f75 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 3 Sep 2014 10:19:57 +0300 Subject: ima: remove unnecessary extra variable 'function' variable value can be changed instead of allocating extra '_func' variable. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_main.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index f7b85bf0eaf3..aaf5552e808d 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -164,7 +164,7 @@ static int process_measurement(struct file *file, const char *filename, struct ima_template_desc *template_desc; char *pathbuf = NULL; const char *pathname = NULL; - int rc = -ENOMEM, action, must_appraise, _func; + int rc = -ENOMEM, action, must_appraise; struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL; int xattr_len = 0; @@ -182,7 +182,8 @@ static int process_measurement(struct file *file, const char *filename, must_appraise = action & IMA_APPRAISE; /* Is the appraise rule hook specific? */ - _func = (action & IMA_FILE_APPRAISE) ? FILE_CHECK : function; + if (action & IMA_FILE_APPRAISE) + function = FILE_CHECK; mutex_lock(&inode->i_mutex); @@ -201,7 +202,7 @@ static int process_measurement(struct file *file, const char *filename, /* Nothing to do, just return existing appraised status */ if (!action) { if (must_appraise) - rc = ima_get_cache_status(iint, _func); + rc = ima_get_cache_status(iint, function); goto out_digsig; } @@ -223,7 +224,7 @@ static int process_measurement(struct file *file, const char *filename, ima_store_measurement(iint, file, pathname, xattr_value, xattr_len); if (action & IMA_APPRAISE_SUBMASK) - rc = ima_appraise_measurement(_func, iint, file, pathname, + rc = ima_appraise_measurement(function, iint, file, pathname, xattr_value, xattr_len, opened); if (action & IMA_AUDIT) ima_audit_measurement(iint, pathname); -- cgit v1.2.3 From e4a9c5196566bd47ac92f6e5ef7f48412ded7176 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 3 Sep 2014 10:19:58 +0300 Subject: ima: add missing '__init' keywords Add missing keywords to the function definition to cleanup to discard initialization code. Signed-off-by: Dmitry Kasatkin Reviewed-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 2 -- security/integrity/ima/ima_crypto.c | 2 +- security/integrity/ima/ima_template.c | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index c6990a723f73..8e4bb883fc13 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -107,8 +107,6 @@ void ima_print_digest(struct seq_file *m, u8 *digest, int size); struct ima_template_desc *ima_template_desc_current(void); int ima_init_template(void); -int ima_init_template(void); - /* * used to protect h_table and sha_table */ diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 3b26472fbf0a..d34e7dfc1118 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -97,7 +97,7 @@ static int ima_kernel_read(struct file *file, loff_t offset, return ret; } -int ima_init_crypto(void) +int __init ima_init_crypto(void) { long rc; diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index a076a967ec47..f6826066ff0c 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -152,7 +152,7 @@ out: return result; } -static int init_defined_templates(void) +static int __init init_defined_templates(void) { int i = 0; int result = 0; @@ -178,7 +178,7 @@ struct ima_template_desc *ima_template_desc_current(void) return ima_template; } -int ima_init_template(void) +int __init ima_init_template(void) { int result; -- cgit v1.2.3 From 86f2bc024966d962d4d7575468e226e2269d198c Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 20 Aug 2014 12:37:57 +0300 Subject: ima: remove unnecessary appraisal test ima_get_action() sets the "action" flags based on policy. Before collecting, measuring, appraising, or auditing the file, the "action" flag is updated based on the cached iint->flags. This patch removes the subsequent unnecessary appraisal test in ima_appraise_measurement(). Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_appraise.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 225fd944a4ef..013ec3f0e42d 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -192,8 +192,6 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, enum integrity_status status = INTEGRITY_UNKNOWN; int rc = xattr_len, hash_start = 0; - if (!ima_appraise) - return 0; if (!inode->i_op->getxattr) return INTEGRITY_UNKNOWN; -- cgit v1.2.3 From 17f4bad3abc7c09f42987d89ccccab02c03455a9 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 19 Aug 2014 16:48:39 +0300 Subject: ima: remove usage of filename parameter In all cases except ima_bprm_check() the filename was not defined and ima_d_path() was used to find the full path. Unfortunately, the bprm filename is a relative pathname (eg. .//filename). ima_bprm_check() selects between bprm->interp and bprm->filename. The following dump demonstrates the differences between using filename and interp. bprm->filename filename: ./foo.sh, pathname: /root/bin/foo.sh filename: ./foo.sh, pathname: /bin/dash bprm->interp filename: ./foo.sh, pathname: /root/bin/foo.sh filename: /bin/sh, pathname: /bin/dash In both cases the pathnames are currently the same. This patch removes usage of filename and interp in favor of d_absolute_path. Changes v3: - 11 extra bytes for "deleted" not needed (Mimi) - purpose "replace relative bprm filename with full pathname" (Mimi) Changes v2: - use d_absolute_path() instead of d_path to work in chroot environments. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_api.c | 5 ++--- security/integrity/ima/ima_main.c | 19 ++++++++----------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index d9cd5ce14d2b..65c41a968cc1 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -330,10 +330,9 @@ const char *ima_d_path(struct path *path, char **pathbuf) { char *pathname = NULL; - /* We will allow 11 spaces for ' (deleted)' to be appended */ - *pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL); + *pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); if (*pathbuf) { - pathname = d_path(path, *pathbuf, PATH_MAX + 11); + pathname = d_absolute_path(path, *pathbuf, PATH_MAX); if (IS_ERR(pathname)) { kfree(*pathbuf); *pathbuf = NULL; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index aaf5552e808d..673a37e92ba3 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -156,8 +156,8 @@ void ima_file_free(struct file *file) ima_check_last_writer(iint, inode, file); } -static int process_measurement(struct file *file, const char *filename, - int mask, int function, int opened) +static int process_measurement(struct file *file, int mask, int function, + int opened) { struct inode *inode = file_inode(file); struct integrity_iint_cache *iint; @@ -218,7 +218,7 @@ static int process_measurement(struct file *file, const char *filename, goto out_digsig; } - pathname = filename ?: ima_d_path(&file->f_path, &pathbuf); + pathname = ima_d_path(&file->f_path, &pathbuf); if (action & IMA_MEASURE) ima_store_measurement(iint, file, pathname, @@ -254,7 +254,7 @@ out: int ima_file_mmap(struct file *file, unsigned long prot) { if (file && (prot & PROT_EXEC)) - return process_measurement(file, NULL, MAY_EXEC, MMAP_CHECK, 0); + return process_measurement(file, MAY_EXEC, MMAP_CHECK, 0); return 0; } @@ -273,10 +273,7 @@ int ima_file_mmap(struct file *file, unsigned long prot) */ int ima_bprm_check(struct linux_binprm *bprm) { - return process_measurement(bprm->file, - (strcmp(bprm->filename, bprm->interp) == 0) ? - bprm->filename : bprm->interp, - MAY_EXEC, BPRM_CHECK, 0); + return process_measurement(bprm->file, MAY_EXEC, BPRM_CHECK, 0); } /** @@ -292,7 +289,7 @@ int ima_bprm_check(struct linux_binprm *bprm) int ima_file_check(struct file *file, int mask, int opened) { ima_rdwr_violation_check(file); - return process_measurement(file, NULL, + return process_measurement(file, mask & (MAY_READ | MAY_WRITE | MAY_EXEC), FILE_CHECK, opened); } @@ -317,7 +314,7 @@ int ima_module_check(struct file *file) #endif return 0; /* We rely on module signature checking */ } - return process_measurement(file, NULL, MAY_EXEC, MODULE_CHECK, 0); + return process_measurement(file, MAY_EXEC, MODULE_CHECK, 0); } int ima_fw_from_file(struct file *file, char *buf, size_t size) @@ -328,7 +325,7 @@ int ima_fw_from_file(struct file *file, char *buf, size_t size) return -EACCES; /* INTEGRITY_UNKNOWN */ return 0; } - return process_measurement(file, NULL, MAY_EXEC, FIRMWARE_CHECK, 0); + return process_measurement(file, MAY_EXEC, FIRMWARE_CHECK, 0); } static int __init init_ima(void) -- cgit v1.2.3 From b4148db51720a6b25a981ea72185312d4b6634fe Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 8 May 2014 11:23:53 +0300 Subject: ima: initialize only required template IMA uses only one template. This patch initializes only required template to avoid unnecessary memory allocations. Signed-off-by: Dmitry Kasatkin Reviewed-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_template.c | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index f6826066ff0c..e854862c9337 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -152,24 +152,6 @@ out: return result; } -static int __init init_defined_templates(void) -{ - int i = 0; - int result = 0; - - /* Init defined templates. */ - for (i = 0; i < ARRAY_SIZE(defined_templates); i++) { - struct ima_template_desc *template = &defined_templates[i]; - - result = template_desc_init_fields(template->fmt, - &(template->fields), - &(template->num_fields)); - if (result < 0) - return result; - } - return result; -} - struct ima_template_desc *ima_template_desc_current(void) { if (!ima_template) @@ -180,11 +162,9 @@ struct ima_template_desc *ima_template_desc_current(void) int __init ima_init_template(void) { - int result; - - result = init_defined_templates(); - if (result < 0) - return result; + struct ima_template_desc *template = ima_template_desc_current(); - return 0; + return template_desc_init_fields(template->fmt, + &(template->fields), + &(template->num_fields)); } -- cgit v1.2.3 From 1ae8f41c23ff6a75c1432faed7281aea5ce7c236 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 17 Apr 2014 14:41:06 +0300 Subject: integrity: move asymmetric keys config option For better visual appearance it is better to co-locate asymmetric key options together with signature support. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/Kconfig | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 245c6d92065b..f79d8537bdda 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -17,6 +17,18 @@ config INTEGRITY_SIGNATURE This is useful for evm and module keyrings, when keys are usually only added from initramfs. +config INTEGRITY_ASYMMETRIC_KEYS + boolean "Enable asymmetric keys support" + depends on INTEGRITY_SIGNATURE + default n + select ASYMMETRIC_KEY_TYPE + select ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select PUBLIC_KEY_ALGO_RSA + select X509_CERTIFICATE_PARSER + help + This option enables digital signature verification using + asymmetric keys. + config INTEGRITY_AUDIT bool "Enables integrity auditing support " depends on INTEGRITY && AUDIT @@ -32,17 +44,5 @@ config INTEGRITY_AUDIT be enabled by specifying 'integrity_audit=1' on the kernel command line. -config INTEGRITY_ASYMMETRIC_KEYS - boolean "Enable asymmetric keys support" - depends on INTEGRITY_SIGNATURE - default n - select ASYMMETRIC_KEY_TYPE - select ASYMMETRIC_PUBLIC_KEY_SUBTYPE - select PUBLIC_KEY_ALGO_RSA - select X509_CERTIFICATE_PARSER - help - This option enables digital signature verification using - asymmetric keys. - source security/integrity/ima/Kconfig source security/integrity/evm/Kconfig -- cgit v1.2.3 From 7ef84e65ecc60289281e8e7e83a8bb6a97d7df5c Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 17 Apr 2014 15:07:15 +0300 Subject: integrity: base integrity subsystem kconfig options on integrity The integrity subsystem has lots of options and takes more than half of the security menu. This patch consolidates the options under "integrity", which are hidden if not enabled. This change does not affect existing configurations. Re-configuration is not needed. Changes v4: - no need to change "integrity subsystem" to menuconfig as options are hidden, when not enabled. (Mimi) - add INTEGRITY Kconfig help description Changes v3: - dependency to INTEGRITY removed when behind 'if INTEGRITY' Changes v2: - previous patch moved integrity out of the 'security' menu. This version keeps integrity as a security option (Mimi). Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/Kconfig | 22 ++++++++++++++++++---- security/integrity/evm/Kconfig | 8 -------- security/integrity/ima/Kconfig | 2 -- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index f79d8537bdda..b76235ae4786 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -1,11 +1,23 @@ # config INTEGRITY - def_bool y - depends on IMA || EVM + bool "Integrity subsystem" + depends on SECURITY + default y + help + This option enables the integrity subsystem, which is comprised + of a number of different components including the Integrity + Measurement Architecture (IMA), Extended Verification Module + (EVM), IMA-appraisal extension, digital signature verification + extension and audit measurement log support. + + Each of these components can be enabled/disabled separately. + Refer to the individual components for additional details. + +if INTEGRITY config INTEGRITY_SIGNATURE boolean "Digital signature verification using multiple keyrings" - depends on INTEGRITY && KEYS + depends on KEYS default n select SIGNATURE help @@ -31,7 +43,7 @@ config INTEGRITY_ASYMMETRIC_KEYS config INTEGRITY_AUDIT bool "Enables integrity auditing support " - depends on INTEGRITY && AUDIT + depends on AUDIT default y help In addition to enabling integrity auditing support, this @@ -46,3 +58,5 @@ config INTEGRITY_AUDIT source security/integrity/ima/Kconfig source security/integrity/evm/Kconfig + +endif # if INTEGRITY diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index d606f3d12d6b..df586fa00ef1 100644 --- a/security/integrity/evm/Kconfig +++ b/security/integrity/evm/Kconfig @@ -1,6 +1,5 @@ config EVM boolean "EVM support" - depends on SECURITY select KEYS select ENCRYPTED_KEYS select CRYPTO_HMAC @@ -12,10 +11,6 @@ config EVM If you are unsure how to answer this question, answer N. -if EVM - -menu "EVM options" - config EVM_ATTR_FSUUID bool "FSUUID (version 2)" default y @@ -47,6 +42,3 @@ config EVM_EXTRA_SMACK_XATTRS additional info to the calculation, requires existing EVM labeled file systems to be relabeled. -endmenu - -endif diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 08758fbd496f..e099875643c5 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -2,8 +2,6 @@ # config IMA bool "Integrity Measurement Architecture(IMA)" - depends on SECURITY - select INTEGRITY select SECURITYFS select CRYPTO select CRYPTO_HMAC -- cgit v1.2.3 From a2d61ed525f3458a913147fd02b1a5cf15e7551b Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 2 Jul 2014 15:42:19 +0300 Subject: integrity: make integrity files as 'integrity' module The kernel print macros use the KBUILD_MODNAME, which is initialized to the module name. The current integrity/Makefile makes every file as its own module, so pr_xxx messages are prefixed with the file name instead of the module. Similar to the evm/Makefile and ima/Makefile, this patch fixes the integrity/Makefile to use the single name 'integrity'. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/security/integrity/Makefile b/security/integrity/Makefile index 0793f4811cb7..8d1f4bf51087 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -3,11 +3,11 @@ # obj-$(CONFIG_INTEGRITY) += integrity.o -obj-$(CONFIG_INTEGRITY_AUDIT) += integrity_audit.o -obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o -obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o integrity-y := iint.o +integrity-$(CONFIG_INTEGRITY_AUDIT) += integrity_audit.o +integrity-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o +integrity-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o subdir-$(CONFIG_IMA) += ima obj-$(CONFIG_IMA) += ima/ -- cgit v1.2.3 From cbe0d6e8794f1da6cac1ea3864d2cfaf0bf87c8e Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 10 Sep 2014 17:09:57 -0400 Subject: selinux: make the netif cache namespace aware While SELinux largely ignores namespaces, for good reason, there are some places where it needs to at least be aware of namespaces in order to function correctly. Network namespaces are one example. Basic awareness of network namespaces are necessary in order to match a network interface's index number to an actual network device. This patch corrects a problem with network interfaces added to a non-init namespace, and can be reproduced with the following commands: [NOTE: the NetLabel configuration is here only to active the dynamic networking controls ] # netlabelctl unlbl add default address:0.0.0.0/0 \ label:system_u:object_r:unlabeled_t:s0 # netlabelctl unlbl add default address:::/0 \ label:system_u:object_r:unlabeled_t:s0 # netlabelctl cipsov4 add pass doi:100 tags:1 # netlabelctl map add domain:lspp_test_netlabel_t \ protocol:cipsov4,100 # ip link add type veth # ip netns add myns # ip link set veth1 netns myns # ip a add dev veth0 10.250.13.100/24 # ip netns exec myns ip a add dev veth1 10.250.13.101/24 # ip l set veth0 up # ip netns exec myns ip l set veth1 up # ping -c 1 10.250.13.101 # ip netns exec myns ping -c 1 10.250.13.100 Reported-by: Jiri Jaburek Signed-off-by: Paul Moore --- security/selinux/hooks.c | 33 ++++++++++++++++-------------- security/selinux/include/netif.h | 4 +++- security/selinux/include/objsec.h | 2 ++ security/selinux/netif.c | 43 +++++++++++++++++++++------------------ 4 files changed, 46 insertions(+), 36 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 50978d3183ea..5eb512b7ac31 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4307,15 +4307,15 @@ static int selinux_socket_unix_may_send(struct socket *sock, &ad); } -static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, - u32 peer_sid, +static int selinux_inet_sys_rcv_skb(struct net *ns, int ifindex, + char *addrp, u16 family, u32 peer_sid, struct common_audit_data *ad) { int err; u32 if_sid; u32 node_sid; - err = sel_netif_sid(ifindex, &if_sid); + err = sel_netif_sid(ns, ifindex, &if_sid); if (err) return err; err = avc_has_perm(peer_sid, if_sid, @@ -4408,8 +4408,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); if (err) return err; - err = selinux_inet_sys_rcv_skb(skb->skb_iif, addrp, family, - peer_sid, &ad); + err = selinux_inet_sys_rcv_skb(sock_net(sk), skb->skb_iif, + addrp, family, peer_sid, &ad); if (err) { selinux_netlbl_err(skb, err, 0); return err; @@ -4748,7 +4748,8 @@ out: #ifdef CONFIG_NETFILTER -static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, +static unsigned int selinux_ip_forward(struct sk_buff *skb, + const struct net_device *indev, u16 family) { int err; @@ -4774,14 +4775,14 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, ad.type = LSM_AUDIT_DATA_NET; ad.u.net = &net; - ad.u.net->netif = ifindex; + ad.u.net->netif = indev->ifindex; ad.u.net->family = family; if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0) return NF_DROP; if (peerlbl_active) { - err = selinux_inet_sys_rcv_skb(ifindex, addrp, family, - peer_sid, &ad); + err = selinux_inet_sys_rcv_skb(dev_net(indev), indev->ifindex, + addrp, family, peer_sid, &ad); if (err) { selinux_netlbl_err(skb, err, 1); return NF_DROP; @@ -4810,7 +4811,7 @@ static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return selinux_ip_forward(skb, in->ifindex, PF_INET); + return selinux_ip_forward(skb, in, PF_INET); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) @@ -4820,7 +4821,7 @@ static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return selinux_ip_forward(skb, in->ifindex, PF_INET6); + return selinux_ip_forward(skb, in, PF_INET6); } #endif /* IPV6 */ @@ -4908,11 +4909,13 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, return NF_ACCEPT; } -static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, +static unsigned int selinux_ip_postroute(struct sk_buff *skb, + const struct net_device *outdev, u16 family) { u32 secmark_perm; u32 peer_sid; + int ifindex = outdev->ifindex; struct sock *sk; struct common_audit_data ad; struct lsm_network_audit net = {0,}; @@ -5025,7 +5028,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, u32 if_sid; u32 node_sid; - if (sel_netif_sid(ifindex, &if_sid)) + if (sel_netif_sid(dev_net(outdev), ifindex, &if_sid)) return NF_DROP; if (avc_has_perm(peer_sid, if_sid, SECCLASS_NETIF, NETIF__EGRESS, &ad)) @@ -5047,7 +5050,7 @@ static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return selinux_ip_postroute(skb, out->ifindex, PF_INET); + return selinux_ip_postroute(skb, out, PF_INET); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) @@ -5057,7 +5060,7 @@ static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return selinux_ip_postroute(skb, out->ifindex, PF_INET6); + return selinux_ip_postroute(skb, out, PF_INET6); } #endif /* IPV6 */ diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h index 57c6eae81eac..c72145444090 100644 --- a/security/selinux/include/netif.h +++ b/security/selinux/include/netif.h @@ -17,9 +17,11 @@ #ifndef _SELINUX_NETIF_H_ #define _SELINUX_NETIF_H_ +#include + void sel_netif_flush(void); -int sel_netif_sid(int ifindex, u32 *sid); +int sel_netif_sid(struct net *ns, int ifindex, u32 *sid); #endif /* _SELINUX_NETIF_H_ */ diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 078e553f52f2..81fa718d5cb3 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "flask.h" #include "avc.h" @@ -78,6 +79,7 @@ struct ipc_security_struct { }; struct netif_security_struct { + struct net *ns; /* network namespace */ int ifindex; /* device index */ u32 sid; /* SID for this interface */ }; diff --git a/security/selinux/netif.c b/security/selinux/netif.c index 3c3de4ca0ebc..485524c477a4 100644 --- a/security/selinux/netif.c +++ b/security/selinux/netif.c @@ -45,6 +45,7 @@ static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE]; /** * sel_netif_hashfn - Hashing function for the interface table + * @ns: the network namespace * @ifindex: the network interface * * Description: @@ -52,13 +53,14 @@ static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE]; * bucket number for the given interface. * */ -static inline u32 sel_netif_hashfn(int ifindex) +static inline u32 sel_netif_hashfn(const struct net *ns, int ifindex) { - return (ifindex & (SEL_NETIF_HASH_SIZE - 1)); + return (((uintptr_t)ns + ifindex) & (SEL_NETIF_HASH_SIZE - 1)); } /** * sel_netif_find - Search for an interface record + * @ns: the network namespace * @ifindex: the network interface * * Description: @@ -66,15 +68,15 @@ static inline u32 sel_netif_hashfn(int ifindex) * If an entry can not be found in the table return NULL. * */ -static inline struct sel_netif *sel_netif_find(int ifindex) +static inline struct sel_netif *sel_netif_find(const struct net *ns, + int ifindex) { - int idx = sel_netif_hashfn(ifindex); + int idx = sel_netif_hashfn(ns, ifindex); struct sel_netif *netif; list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list) - /* all of the devices should normally fit in the hash, so we - * optimize for that case */ - if (likely(netif->nsec.ifindex == ifindex)) + if (net_eq(netif->nsec.ns, ns) && + netif->nsec.ifindex == ifindex) return netif; return NULL; @@ -96,7 +98,7 @@ static int sel_netif_insert(struct sel_netif *netif) if (sel_netif_total >= SEL_NETIF_HASH_MAX) return -ENOSPC; - idx = sel_netif_hashfn(netif->nsec.ifindex); + idx = sel_netif_hashfn(netif->nsec.ns, netif->nsec.ifindex); list_add_rcu(&netif->list, &sel_netif_hash[idx]); sel_netif_total++; @@ -120,6 +122,7 @@ static void sel_netif_destroy(struct sel_netif *netif) /** * sel_netif_sid_slow - Lookup the SID of a network interface using the policy + * @ns: the network namespace * @ifindex: the network interface * @sid: interface SID * @@ -130,7 +133,7 @@ static void sel_netif_destroy(struct sel_netif *netif) * failure. * */ -static int sel_netif_sid_slow(int ifindex, u32 *sid) +static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid) { int ret; struct sel_netif *netif; @@ -140,7 +143,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid) /* NOTE: we always use init's network namespace since we don't * currently support containers */ - dev = dev_get_by_index(&init_net, ifindex); + dev = dev_get_by_index(ns, ifindex); if (unlikely(dev == NULL)) { printk(KERN_WARNING "SELinux: failure in sel_netif_sid_slow()," @@ -149,7 +152,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid) } spin_lock_bh(&sel_netif_lock); - netif = sel_netif_find(ifindex); + netif = sel_netif_find(ns, ifindex); if (netif != NULL) { *sid = netif->nsec.sid; ret = 0; @@ -163,6 +166,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid) ret = security_netif_sid(dev->name, &new->nsec.sid); if (ret != 0) goto out; + new->nsec.ns = ns; new->nsec.ifindex = ifindex; ret = sel_netif_insert(new); if (ret != 0) @@ -184,6 +188,7 @@ out: /** * sel_netif_sid - Lookup the SID of a network interface + * @ns: the network namespace * @ifindex: the network interface * @sid: interface SID * @@ -195,12 +200,12 @@ out: * on failure. * */ -int sel_netif_sid(int ifindex, u32 *sid) +int sel_netif_sid(struct net *ns, int ifindex, u32 *sid) { struct sel_netif *netif; rcu_read_lock(); - netif = sel_netif_find(ifindex); + netif = sel_netif_find(ns, ifindex); if (likely(netif != NULL)) { *sid = netif->nsec.sid; rcu_read_unlock(); @@ -208,11 +213,12 @@ int sel_netif_sid(int ifindex, u32 *sid) } rcu_read_unlock(); - return sel_netif_sid_slow(ifindex, sid); + return sel_netif_sid_slow(ns, ifindex, sid); } /** * sel_netif_kill - Remove an entry from the network interface table + * @ns: the network namespace * @ifindex: the network interface * * Description: @@ -220,13 +226,13 @@ int sel_netif_sid(int ifindex, u32 *sid) * table if it exists. * */ -static void sel_netif_kill(int ifindex) +static void sel_netif_kill(const struct net *ns, int ifindex) { struct sel_netif *netif; rcu_read_lock(); spin_lock_bh(&sel_netif_lock); - netif = sel_netif_find(ifindex); + netif = sel_netif_find(ns, ifindex); if (netif) sel_netif_destroy(netif); spin_unlock_bh(&sel_netif_lock); @@ -257,11 +263,8 @@ static int sel_netif_netdev_notifier_handler(struct notifier_block *this, { struct net_device *dev = netdev_notifier_info_to_dev(ptr); - if (dev_net(dev) != &init_net) - return NOTIFY_DONE; - if (event == NETDEV_DOWN) - sel_netif_kill(dev->ifindex); + sel_netif_kill(dev_net(dev), dev->ifindex); return NOTIFY_DONE; } -- cgit v1.2.3 From 8da79b6439f46a2bea71238ab2f0e84e3c16587d Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:07:07 +0100 Subject: KEYS: Fix missing statics Fix missing statics (found by checker). Signed-off-by: David Howells Acked-by: Vivek Goyal --- security/keys/request_key_auth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 842e6f410d50..739e7455d388 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -44,12 +44,12 @@ struct key_type key_type_request_key_auth = { .read = request_key_auth_read, }; -int request_key_auth_preparse(struct key_preparsed_payload *prep) +static int request_key_auth_preparse(struct key_preparsed_payload *prep) { return 0; } -void request_key_auth_free_preparse(struct key_preparsed_payload *prep) +static void request_key_auth_free_preparse(struct key_preparsed_payload *prep) { } -- cgit v1.2.3 From 15155b9a4530f8da42d6594927928e25b2b29875 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:07:07 +0100 Subject: PKCS#7: Add a missing static Add a missing static (found by checker). Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/pkcs7_trust.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index e666eb011a85..fad888ea4fad 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -23,9 +23,9 @@ /** * Check the trust on one PKCS#7 SignedInfo block. */ -int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, - struct pkcs7_signed_info *sinfo, - struct key *trust_keyring) +static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, + struct pkcs7_signed_info *sinfo, + struct key *trust_keyring) { struct public_key_signature *sig = &sinfo->sig; struct x509_certificate *x509, *last = NULL, *p; -- cgit v1.2.3 From 54e2c2c1a9d6cbb270b0999a38545fa9a69bee43 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:29:03 +0100 Subject: KEYS: Reinstate EPERM for a key type name beginning with a '.' Reinstate the generation of EPERM for a key type name beginning with a '.' in a userspace call. Types whose name begins with a '.' are internal only. The test was removed by: commit a4e3b8d79a5c6d40f4a9703abf7fe3abcc6c3b8d Author: Mimi Zohar Date: Thu May 22 14:02:23 2014 -0400 Subject: KEYS: special dot prefixed keyring name bug fix I think we want to keep the restriction on type name so that userspace can't add keys of a special internal type. Note that removal of the test causes several of the tests in the keyutils testsuite to fail. Signed-off-by: David Howells Acked-by: Vivek Goyal cc: Mimi Zohar --- security/keys/keyctl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index e26f860e5f2e..eff88a5f5d40 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -37,6 +37,8 @@ static int key_get_type_from_user(char *type, return ret; if (ret == 0 || ret >= len) return -EINVAL; + if (type[0] == '.') + return -EPERM; type[len - 1] = '\0'; return 0; } -- cgit v1.2.3 From 3cd0920cde6a726b9965b60184f9d5a54358304d Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:29:03 +0100 Subject: PKCS#7: Provide a single place to do signed info block freeing The code to free a signed info block is repeated several times, so move the code to do it into a function of its own. This gives us a place to add clean ups for stuff that gets added to pkcs7_signed_info. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/pkcs7_parser.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 42e56aa7d277..4c4ea35c338b 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -31,6 +31,18 @@ struct pkcs7_parse_context { unsigned sinfo_index; }; +/* + * Free a signed information block. + */ +static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo) +{ + if (sinfo) { + mpi_free(sinfo->sig.mpi[0]); + kfree(sinfo->sig.digest); + kfree(sinfo); + } +} + /** * pkcs7_free_message - Free a PKCS#7 message * @pkcs7: The PKCS#7 message to free @@ -54,9 +66,7 @@ void pkcs7_free_message(struct pkcs7_message *pkcs7) while (pkcs7->signed_infos) { sinfo = pkcs7->signed_infos; pkcs7->signed_infos = sinfo->next; - mpi_free(sinfo->sig.mpi[0]); - kfree(sinfo->sig.digest); - kfree(sinfo); + pkcs7_free_signed_info(sinfo); } kfree(pkcs7); } @@ -100,16 +110,12 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) ctx->certs = cert->next; x509_free_certificate(cert); } - mpi_free(ctx->sinfo->sig.mpi[0]); - kfree(ctx->sinfo->sig.digest); - kfree(ctx->sinfo); + pkcs7_free_signed_info(ctx->sinfo); kfree(ctx); return msg; error_decode: - mpi_free(ctx->sinfo->sig.mpi[0]); - kfree(ctx->sinfo->sig.digest); - kfree(ctx->sinfo); + pkcs7_free_signed_info(ctx->sinfo); error_no_sinfo: kfree(ctx); error_no_ctx: -- cgit v1.2.3 From cecf5d2e1208da512a4c951c24acd66c54a4d06c Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:29:03 +0100 Subject: PKCS#7: Fix the parser cleanup to drain parsed out X.509 certs Fix the parser cleanup code to drain parsed out X.509 certs in the case that the decode fails and we jump to error_decode. The function is rearranged so that the same cleanup code is used in the success case as the error case - just that the message descriptor under construction is only released if it is still pointed to by the context struct at that point. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/pkcs7_parser.c | 39 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 4c4ea35c338b..1e9861da7ee4 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -81,47 +81,46 @@ EXPORT_SYMBOL_GPL(pkcs7_free_message); struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) { struct pkcs7_parse_context *ctx; - struct pkcs7_message *msg; - long ret; + struct pkcs7_message *msg = ERR_PTR(-ENOMEM); + int ret; - ret = -ENOMEM; - msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL); - if (!msg) - goto error_no_sig; ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL); if (!ctx) - goto error_no_ctx; + goto out_no_ctx; + ctx->msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL); + if (!ctx->msg) + goto out_no_msg; ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); if (!ctx->sinfo) - goto error_no_sinfo; + goto out_no_sinfo; - ctx->msg = msg; ctx->data = (unsigned long)data; ctx->ppcerts = &ctx->certs; ctx->ppsinfo = &ctx->msg->signed_infos; /* Attempt to decode the signature */ ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen); - if (ret < 0) - goto error_decode; + if (ret < 0) { + msg = ERR_PTR(ret); + goto out; + } + + msg = ctx->msg; + ctx->msg = NULL; +out: while (ctx->certs) { struct x509_certificate *cert = ctx->certs; ctx->certs = cert->next; x509_free_certificate(cert); } pkcs7_free_signed_info(ctx->sinfo); +out_no_sinfo: + pkcs7_free_message(ctx->msg); +out_no_msg: kfree(ctx); +out_no_ctx: return msg; - -error_decode: - pkcs7_free_signed_info(ctx->sinfo); -error_no_sinfo: - kfree(ctx); -error_no_ctx: - pkcs7_free_message(msg); -error_no_sig: - return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(pkcs7_parse_message); -- cgit v1.2.3 From 53d91c5ce0cb8945b55e8bb54e551cabc51eb28d Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:01 +0100 Subject: Provide a binary to hex conversion function Provide a function to convert a buffer of binary data into an unterminated ascii hex string representation of that data. Signed-off-by: David Howells Acked-by: Vivek Goyal --- include/linux/kernel.h | 1 + lib/hexdump.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 4c52907a6d8b..89a0b8e5a952 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -500,6 +500,7 @@ static inline char * __deprecated pack_hex_byte(char *buf, u8 byte) extern int hex_to_bin(char ch); extern int __must_check hex2bin(u8 *dst, const char *src, size_t count); +extern char *bin2hex(char *dst, const void *src, size_t count); int mac_pton(const char *s, u8 *mac); diff --git a/lib/hexdump.c b/lib/hexdump.c index 8499c810909a..270773b91923 100644 --- a/lib/hexdump.c +++ b/lib/hexdump.c @@ -58,6 +58,22 @@ int hex2bin(u8 *dst, const char *src, size_t count) } EXPORT_SYMBOL(hex2bin); +/** + * bin2hex - convert binary data to an ascii hexadecimal string + * @dst: ascii hexadecimal result + * @src: binary data + * @count: binary data length + */ +char *bin2hex(char *dst, const void *src, size_t count) +{ + const unsigned char *_src = src; + + while (count--) + dst = hex_byte_pack(dst, *_src++); + return dst; +} +EXPORT_SYMBOL(bin2hex); + /** * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory * @buf: data blob to dump -- cgit v1.2.3 From 462919591a1791e76042dc5c1e0148715df59beb Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:02 +0100 Subject: KEYS: Preparse match data Preparse the match data. This provides several advantages: (1) The preparser can reject invalid criteria up front. (2) The preparser can convert the criteria to binary data if necessary (the asymmetric key type really wants to do binary comparison of the key IDs). (3) The preparser can set the type of search to be performed. This means that it's not then a one-off setting in the key type. (4) The preparser can set an appropriate comparator function. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/asymmetric_type.c | 31 +++++++++++++++++++- include/keys/user-type.h | 4 ++- include/linux/key-type.h | 31 ++++++++++++++++++-- net/dns_resolver/dns_key.c | 5 ++-- security/keys/internal.h | 8 ++---- security/keys/keyring.c | 49 +++++++++++++++++++------------- security/keys/proc.c | 8 +++--- security/keys/process_keys.c | 13 +++++---- security/keys/request_key.c | 21 ++++++++++---- security/keys/request_key_auth.c | 6 ++-- security/keys/user_defined.c | 4 +-- 11 files changed, 129 insertions(+), 51 deletions(-) diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index eb8cd46961a5..f666b4e8d256 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -59,9 +59,11 @@ EXPORT_SYMBOL_GPL(asymmetric_keyid_match); * "id:" - request a key matching the ID * ":" - request a key of a subtype */ -static int asymmetric_key_match(const struct key *key, const void *description) +static int asymmetric_key_match(const struct key *key, + const struct key_match_data *match_data) { const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); + const char *description = match_data->raw_data; const char *spec = description; const char *id; ptrdiff_t speclen; @@ -93,6 +95,31 @@ static int asymmetric_key_match(const struct key *key, const void *description) return 0; } +/* + * Preparse the match criterion. If we don't set lookup_type and cmp, + * the default will be an exact match on the key description. + * + * There are some specifiers for matching key IDs rather than by the key + * description: + * + * "id:" - request a key by any available ID + * + * These have to be searched by iteration rather than by direct lookup because + * the key is hashed according to its description. + */ +static int asymmetric_key_match_preparse(struct key_match_data *match_data) +{ + match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; + return 0; +} + +/* + * Free the preparsed the match criterion. + */ +static void asymmetric_key_match_free(struct key_match_data *match_data) +{ +} + /* * Describe the asymmetric key */ @@ -196,7 +223,9 @@ struct key_type key_type_asymmetric = { .preparse = asymmetric_key_preparse, .free_preparse = asymmetric_key_free_preparse, .instantiate = generic_key_instantiate, + .match_preparse = asymmetric_key_match_preparse, .match = asymmetric_key_match, + .match_free = asymmetric_key_match_free, .destroy = asymmetric_key_destroy, .describe = asymmetric_key_describe, .def_lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE, diff --git a/include/keys/user-type.h b/include/keys/user-type.h index 3ab1873a4bfa..66d92af30e7c 100644 --- a/include/keys/user-type.h +++ b/include/keys/user-type.h @@ -36,11 +36,13 @@ extern struct key_type key_type_user; extern struct key_type key_type_logon; struct key_preparsed_payload; +struct key_match_data; extern int user_preparse(struct key_preparsed_payload *prep); extern void user_free_preparse(struct key_preparsed_payload *prep); extern int user_update(struct key *key, struct key_preparsed_payload *prep); -extern int user_match(const struct key *key, const void *criterion); +extern int user_match(const struct key *key, + const struct key_match_data *match_data); extern void user_revoke(struct key *key); extern void user_destroy(struct key *key); extern void user_describe(const struct key *user, struct seq_file *m); diff --git a/include/linux/key-type.h b/include/linux/key-type.h index 44792ee649de..8aba688a451a 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -52,6 +52,22 @@ struct key_preparsed_payload { typedef int (*request_key_actor_t)(struct key_construction *key, const char *op, void *aux); +/* + * Preparsed matching criterion. + */ +struct key_match_data { + /* Comparison function, defaults to type->match, but can be replaced by + * type->match_preparse(). */ + int (*cmp)(const struct key *key, + const struct key_match_data *match_data); + + const void *raw_data; /* Raw match data */ + void *preparsed; /* For ->match_preparse() to stash stuff */ + unsigned lookup_type; /* Type of lookup for this search. */ +#define KEYRING_SEARCH_LOOKUP_DIRECT 0x0000 /* Direct lookup by description. */ +#define KEYRING_SEARCH_LOOKUP_ITERATE 0x0001 /* Iterative search. */ +}; + /* * kernel managed key type definition */ @@ -67,8 +83,6 @@ struct key_type { /* Default key search algorithm. */ unsigned def_lookup_type; -#define KEYRING_SEARCH_LOOKUP_DIRECT 0x0000 /* Direct lookup by description. */ -#define KEYRING_SEARCH_LOOKUP_ITERATE 0x0001 /* Iterative search. */ /* vet a description */ int (*vet_description)(const char *description); @@ -96,8 +110,19 @@ struct key_type { */ int (*update)(struct key *key, struct key_preparsed_payload *prep); + /* Preparse the data supplied to ->match() (optional). The + * data to be preparsed can be found in match_data->raw_data. + * The lookup type can also be set by this function. + */ + int (*match_preparse)(struct key_match_data *match_data); + /* match a key against a description */ - int (*match)(const struct key *key, const void *desc); + int (*match)(const struct key *key, + const struct key_match_data *match_data); + + /* Free preparsed match data (optional). This should be supplied it + * ->match_preparse() is supplied. */ + void (*match_free)(struct key_match_data *match_data); /* clear some of the data from a key on revokation (optional) * - the key's semaphore will be write-locked by the caller diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index f380b2c58178..92df6e508ae7 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -177,10 +177,11 @@ static void dns_resolver_free_preparse(struct key_preparsed_payload *prep) * should end with a period). The domain name is case-independent. */ static int -dns_resolver_match(const struct key *key, const void *description) +dns_resolver_match(const struct key *key, + const struct key_match_data *match_data) { int slen, dlen, ret = 0; - const char *src = key->description, *dsp = description; + const char *src = key->description, *dsp = match_data->raw_data; kenter("%s,%s", src, dsp); diff --git a/security/keys/internal.h b/security/keys/internal.h index 5f20da01fd8d..805e60b0b87e 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -107,13 +107,10 @@ extern int iterate_over_keyring(const struct key *keyring, int (*func)(const struct key *key, void *data), void *data); -typedef int (*key_match_func_t)(const struct key *, const void *); - struct keyring_search_context { struct keyring_index_key index_key; const struct cred *cred; - key_match_func_t match; - const void *match_data; + struct key_match_data match_data; unsigned flags; #define KEYRING_SEARCH_LOOKUP_TYPE 0x0001 /* [as type->def_lookup_type] */ #define KEYRING_SEARCH_NO_STATE_CHECK 0x0002 /* Skip state checks */ @@ -152,7 +149,8 @@ extern struct key *request_key_and_link(struct key_type *type, struct key *dest_keyring, unsigned long flags); -extern int lookup_user_key_possessed(const struct key *key, const void *target); +extern int lookup_user_key_possessed(const struct key *key, + const struct key_match_data *match_data); extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, key_perm_t perm); #define KEY_LOOKUP_CREATE 0x01 diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 8314a7d2104d..10f0a5f2d362 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -545,7 +545,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data) } /* keys that don't match */ - if (!ctx->match(key, ctx->match_data)) { + if (!ctx->match_data.cmp(key, &ctx->match_data)) { kleave(" = 0 [!match]"); return 0; } @@ -585,8 +585,7 @@ skipped: */ static int search_keyring(struct key *keyring, struct keyring_search_context *ctx) { - if ((ctx->flags & KEYRING_SEARCH_LOOKUP_TYPE) == - KEYRING_SEARCH_LOOKUP_DIRECT) { + if (ctx->match_data.lookup_type == KEYRING_SEARCH_LOOKUP_DIRECT) { const void *object; object = assoc_array_find(&keyring->keys, @@ -627,7 +626,7 @@ static bool search_nested_keyrings(struct key *keyring, /* Check to see if this top-level keyring is what we are looking for * and whether it is valid or not. */ - if (ctx->flags & KEYRING_SEARCH_LOOKUP_ITERATE || + if (ctx->match_data.lookup_type == KEYRING_SEARCH_LOOKUP_ITERATE || keyring_compare_object(keyring, &ctx->index_key)) { ctx->skipped_ret = 2; ctx->flags |= KEYRING_SEARCH_DO_STATE_CHECK; @@ -885,16 +884,28 @@ key_ref_t keyring_search(key_ref_t keyring, .index_key.type = type, .index_key.description = description, .cred = current_cred(), - .match = type->match, - .match_data = description, - .flags = (type->def_lookup_type | - KEYRING_SEARCH_DO_STATE_CHECK), + .match_data.cmp = type->match, + .match_data.raw_data = description, + .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .flags = KEYRING_SEARCH_DO_STATE_CHECK, }; + key_ref_t key; + int ret; - if (!ctx.match) + if (!ctx.match_data.cmp) return ERR_PTR(-ENOKEY); - return keyring_search_aux(keyring, &ctx); + if (type->match_preparse) { + ret = type->match_preparse(&ctx.match_data); + if (ret < 0) + return ERR_PTR(ret); + } + + key = keyring_search_aux(keyring, &ctx); + + if (type->match_free) + type->match_free(&ctx.match_data); + return key; } EXPORT_SYMBOL(keyring_search); @@ -1014,7 +1025,7 @@ static int keyring_detect_cycle_iterator(const void *object, /* We might get a keyring with matching index-key that is nonetheless a * different keyring. */ - if (key != ctx->match_data) + if (key != ctx->match_data.raw_data) return 0; ctx->result = ERR_PTR(-EDEADLK); @@ -1031,14 +1042,14 @@ static int keyring_detect_cycle_iterator(const void *object, static int keyring_detect_cycle(struct key *A, struct key *B) { struct keyring_search_context ctx = { - .index_key = A->index_key, - .match_data = A, - .iterator = keyring_detect_cycle_iterator, - .flags = (KEYRING_SEARCH_LOOKUP_DIRECT | - KEYRING_SEARCH_NO_STATE_CHECK | - KEYRING_SEARCH_NO_UPDATE_TIME | - KEYRING_SEARCH_NO_CHECK_PERM | - KEYRING_SEARCH_DETECT_TOO_DEEP), + .index_key = A->index_key, + .match_data.raw_data = A, + .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .iterator = keyring_detect_cycle_iterator, + .flags = (KEYRING_SEARCH_NO_STATE_CHECK | + KEYRING_SEARCH_NO_UPDATE_TIME | + KEYRING_SEARCH_NO_CHECK_PERM | + KEYRING_SEARCH_DETECT_TOO_DEEP), }; rcu_read_lock(); diff --git a/security/keys/proc.c b/security/keys/proc.c index d3f6f2fd21db..972eeb336b81 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -194,10 +194,10 @@ static int proc_keys_show(struct seq_file *m, void *v) .index_key.type = key->type, .index_key.description = key->description, .cred = current_cred(), - .match = lookup_user_key_possessed, - .match_data = key, - .flags = (KEYRING_SEARCH_NO_STATE_CHECK | - KEYRING_SEARCH_LOOKUP_DIRECT), + .match_data.cmp = lookup_user_key_possessed, + .match_data.raw_data = key, + .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .flags = KEYRING_SEARCH_NO_STATE_CHECK, }; key_ref = make_key_ref(key, 0); diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 0cf8a130a267..08bd533d014f 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -489,9 +489,10 @@ found: /* * See if the key we're looking at is the target key. */ -int lookup_user_key_possessed(const struct key *key, const void *target) +int lookup_user_key_possessed(const struct key *key, + const struct key_match_data *match_data) { - return key == target; + return key == match_data->raw_data; } /* @@ -516,9 +517,9 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags, key_perm_t perm) { struct keyring_search_context ctx = { - .match = lookup_user_key_possessed, - .flags = (KEYRING_SEARCH_NO_STATE_CHECK | - KEYRING_SEARCH_LOOKUP_DIRECT), + .match_data.cmp = lookup_user_key_possessed, + .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .flags = KEYRING_SEARCH_NO_STATE_CHECK, }; struct request_key_auth *rka; struct key *key; @@ -673,7 +674,7 @@ try_again: ctx.index_key.type = key->type; ctx.index_key.description = key->description; ctx.index_key.desc_len = strlen(key->description); - ctx.match_data = key; + ctx.match_data.raw_data = key; kdebug("check possessed"); skey_ref = search_process_keyrings(&ctx); kdebug("possessed=%p", skey_ref); diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 381411941cc1..408523e5e2e2 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -531,9 +531,9 @@ struct key *request_key_and_link(struct key_type *type, .index_key.type = type, .index_key.description = description, .cred = current_cred(), - .match = type->match, - .match_data = description, - .flags = KEYRING_SEARCH_LOOKUP_DIRECT, + .match_data.cmp = type->match, + .match_data.raw_data = description, + .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, }; struct key *key; key_ref_t key_ref; @@ -543,6 +543,14 @@ struct key *request_key_and_link(struct key_type *type, ctx.index_key.type->name, ctx.index_key.description, callout_info, callout_len, aux, dest_keyring, flags); + if (type->match_preparse) { + ret = type->match_preparse(&ctx.match_data); + if (ret < 0) { + key = ERR_PTR(ret); + goto error; + } + } + /* search all the process keyrings for a key */ key_ref = search_process_keyrings(&ctx); @@ -555,7 +563,7 @@ struct key *request_key_and_link(struct key_type *type, if (ret < 0) { key_put(key); key = ERR_PTR(ret); - goto error; + goto error_free; } } } else if (PTR_ERR(key_ref) != -EAGAIN) { @@ -565,12 +573,15 @@ struct key *request_key_and_link(struct key_type *type, * should consult userspace if we can */ key = ERR_PTR(-ENOKEY); if (!callout_info) - goto error; + goto error_free; key = construct_key_and_link(&ctx, callout_info, callout_len, aux, dest_keyring, flags); } +error_free: + if (type->match_free) + type->match_free(&ctx.match_data); error: kleave(" = %p", key); return key; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 739e7455d388..9ae02819cc06 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -246,9 +246,9 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id) .index_key.type = &key_type_request_key_auth, .index_key.description = description, .cred = current_cred(), - .match = user_match, - .match_data = description, - .flags = KEYRING_SEARCH_LOOKUP_DIRECT, + .match_data.cmp = user_match, + .match_data.raw_data = description, + .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, }; struct key *authkey; key_ref_t authkey_ref; diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index eee340011f2b..ec8a56063b02 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -141,9 +141,9 @@ EXPORT_SYMBOL_GPL(user_update); /* * match users on their name */ -int user_match(const struct key *key, const void *description) +int user_match(const struct key *key, const struct key_match_data *match_data) { - return strcmp(key->description, description) == 0; + return strcmp(key->description, match_data->raw_data) == 0; } EXPORT_SYMBOL_GPL(user_match); -- cgit v1.2.3 From 614d8c39014c185aa0f7254f0a470cc33fc1b284 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:04 +0100 Subject: KEYS: Remove key_type::def_lookup_type Remove key_type::def_lookup_type as it's no longer used. The information now defaults to KEYRING_SEARCH_LOOKUP_DIRECT but may be overridden by type->match_preparse(). Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/asymmetric_type.c | 1 - crypto/asymmetric_keys/pkcs7_key_type.c | 1 - include/linux/key-type.h | 3 --- security/keys/big_key.c | 1 - security/keys/internal.h | 11 +++++------ security/keys/user_defined.c | 2 -- 6 files changed, 5 insertions(+), 14 deletions(-) diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index f666b4e8d256..9d78ad7754d9 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -228,7 +228,6 @@ struct key_type key_type_asymmetric = { .match_free = asymmetric_key_match_free, .destroy = asymmetric_key_destroy, .describe = asymmetric_key_describe, - .def_lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE, }; EXPORT_SYMBOL_GPL(key_type_asymmetric); diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c index 3de5fb011de0..d1faa1df1dec 100644 --- a/crypto/asymmetric_keys/pkcs7_key_type.c +++ b/crypto/asymmetric_keys/pkcs7_key_type.c @@ -72,7 +72,6 @@ error: */ static struct key_type key_type_pkcs7 = { .name = "pkcs7_test", - .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, .preparse = pkcs7_preparse, .free_preparse = user_free_preparse, .instantiate = generic_key_instantiate, diff --git a/include/linux/key-type.h b/include/linux/key-type.h index 8aba688a451a..bf93ea609273 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -81,9 +81,6 @@ struct key_type { */ size_t def_datalen; - /* Default key search algorithm. */ - unsigned def_lookup_type; - /* vet a description */ int (*vet_description)(const char *description); diff --git a/security/keys/big_key.c b/security/keys/big_key.c index c2f91a0cf889..4045c13a761a 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -33,7 +33,6 @@ MODULE_LICENSE("GPL"); */ struct key_type key_type_big_key = { .name = "big_key", - .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, .preparse = big_key_preparse, .free_preparse = big_key_free_preparse, .instantiate = generic_key_instantiate, diff --git a/security/keys/internal.h b/security/keys/internal.h index 805e60b0b87e..b47cc532be1e 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -112,12 +112,11 @@ struct keyring_search_context { const struct cred *cred; struct key_match_data match_data; unsigned flags; -#define KEYRING_SEARCH_LOOKUP_TYPE 0x0001 /* [as type->def_lookup_type] */ -#define KEYRING_SEARCH_NO_STATE_CHECK 0x0002 /* Skip state checks */ -#define KEYRING_SEARCH_DO_STATE_CHECK 0x0004 /* Override NO_STATE_CHECK */ -#define KEYRING_SEARCH_NO_UPDATE_TIME 0x0008 /* Don't update times */ -#define KEYRING_SEARCH_NO_CHECK_PERM 0x0010 /* Don't check permissions */ -#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0020 /* Give an error on excessive depth */ +#define KEYRING_SEARCH_NO_STATE_CHECK 0x0001 /* Skip state checks */ +#define KEYRING_SEARCH_DO_STATE_CHECK 0x0002 /* Override NO_STATE_CHECK */ +#define KEYRING_SEARCH_NO_UPDATE_TIME 0x0004 /* Don't update times */ +#define KEYRING_SEARCH_NO_CHECK_PERM 0x0008 /* Don't check permissions */ +#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0010 /* Give an error on excessive depth */ int (*iterator)(const void *object, void *iterator_data); diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index ec8a56063b02..cd7e726e8646 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -26,7 +26,6 @@ static int logon_vet_description(const char *desc); */ struct key_type key_type_user = { .name = "user", - .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, .preparse = user_preparse, .free_preparse = user_free_preparse, .instantiate = generic_key_instantiate, @@ -48,7 +47,6 @@ EXPORT_SYMBOL_GPL(key_type_user); */ struct key_type key_type_logon = { .name = "logon", - .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, .preparse = user_preparse, .free_preparse = user_free_preparse, .instantiate = generic_key_instantiate, -- cgit v1.2.3 From c06cfb08b88dfbe13be44a69ae2fdc3a7c902d81 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:06 +0100 Subject: KEYS: Remove key_type::match in favour of overriding default by match_preparse A previous patch added a ->match_preparse() method to the key type. This is allowed to override the function called by the iteration algorithm. Therefore, we can just set a default that simply checks for an exact match of the key description with the original criterion data and allow match_preparse to override it as needed. The key_type::match op is then redundant and can be removed, as can the user_match() function. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/asymmetric_type.c | 6 +++--- crypto/asymmetric_keys/pkcs7_key_type.c | 1 - fs/cifs/cifs_spnego.c | 1 - fs/cifs/cifsacl.c | 1 - fs/nfs/idmap.c | 2 -- include/keys/user-type.h | 3 --- include/linux/key-type.h | 4 ---- net/ceph/crypto.c | 1 - net/dns_resolver/dns_key.c | 17 +++++++++++++---- net/rxrpc/ar-key.c | 2 -- security/keys/big_key.c | 1 - security/keys/encrypted-keys/encrypted.c | 1 - security/keys/internal.h | 2 ++ security/keys/key.c | 2 +- security/keys/keyring.c | 15 ++++++++++----- security/keys/request_key.c | 2 +- security/keys/request_key_auth.c | 2 +- security/keys/trusted.c | 1 - security/keys/user_defined.c | 12 ------------ 19 files changed, 31 insertions(+), 45 deletions(-) diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 9d78ad7754d9..7c0498968975 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -59,8 +59,8 @@ EXPORT_SYMBOL_GPL(asymmetric_keyid_match); * "id:" - request a key matching the ID * ":" - request a key of a subtype */ -static int asymmetric_key_match(const struct key *key, - const struct key_match_data *match_data) +static int asymmetric_key_cmp(const struct key *key, + const struct key_match_data *match_data) { const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); const char *description = match_data->raw_data; @@ -110,6 +110,7 @@ static int asymmetric_key_match(const struct key *key, static int asymmetric_key_match_preparse(struct key_match_data *match_data) { match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; + match_data->cmp = asymmetric_key_cmp; return 0; } @@ -224,7 +225,6 @@ struct key_type key_type_asymmetric = { .free_preparse = asymmetric_key_free_preparse, .instantiate = generic_key_instantiate, .match_preparse = asymmetric_key_match_preparse, - .match = asymmetric_key_match, .match_free = asymmetric_key_match_free, .destroy = asymmetric_key_destroy, .describe = asymmetric_key_describe, diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c index d1faa1df1dec..751f8fd7335d 100644 --- a/crypto/asymmetric_keys/pkcs7_key_type.c +++ b/crypto/asymmetric_keys/pkcs7_key_type.c @@ -75,7 +75,6 @@ static struct key_type key_type_pkcs7 = { .preparse = pkcs7_preparse, .free_preparse = user_free_preparse, .instantiate = generic_key_instantiate, - .match = user_match, .revoke = user_revoke, .destroy = user_destroy, .describe = user_describe, diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index a3e932547617..f4cf200b3c76 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -62,7 +62,6 @@ cifs_spnego_key_destroy(struct key *key) struct key_type cifs_spnego_key_type = { .name = "cifs.spnego", .instantiate = cifs_spnego_key_instantiate, - .match = user_match, .destroy = cifs_spnego_key_destroy, .describe = user_describe, }; diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 7ff866dbb89e..6d00c419cbae 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -84,7 +84,6 @@ static struct key_type cifs_idmap_key_type = { .instantiate = cifs_idmap_key_instantiate, .destroy = cifs_idmap_key_destroy, .describe = user_describe, - .match = user_match, }; static char * diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 7dd55b745c4d..2f5db844c172 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -177,7 +177,6 @@ static struct key_type key_type_id_resolver = { .preparse = user_preparse, .free_preparse = user_free_preparse, .instantiate = generic_key_instantiate, - .match = user_match, .revoke = user_revoke, .destroy = user_destroy, .describe = user_describe, @@ -401,7 +400,6 @@ static struct key_type key_type_id_resolver_legacy = { .preparse = user_preparse, .free_preparse = user_free_preparse, .instantiate = generic_key_instantiate, - .match = user_match, .revoke = user_revoke, .destroy = user_destroy, .describe = user_describe, diff --git a/include/keys/user-type.h b/include/keys/user-type.h index 66d92af30e7c..cebefb069c44 100644 --- a/include/keys/user-type.h +++ b/include/keys/user-type.h @@ -36,13 +36,10 @@ extern struct key_type key_type_user; extern struct key_type key_type_logon; struct key_preparsed_payload; -struct key_match_data; extern int user_preparse(struct key_preparsed_payload *prep); extern void user_free_preparse(struct key_preparsed_payload *prep); extern int user_update(struct key *key, struct key_preparsed_payload *prep); -extern int user_match(const struct key *key, - const struct key_match_data *match_data); extern void user_revoke(struct key *key); extern void user_destroy(struct key *key); extern void user_describe(const struct key *user, struct seq_file *m); diff --git a/include/linux/key-type.h b/include/linux/key-type.h index bf93ea609273..c14816bd3b44 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -113,10 +113,6 @@ struct key_type { */ int (*match_preparse)(struct key_match_data *match_data); - /* match a key against a description */ - int (*match)(const struct key *key, - const struct key_match_data *match_data); - /* Free preparsed match data (optional). This should be supplied it * ->match_preparse() is supplied. */ void (*match_free)(struct key_match_data *match_data); diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c index ffeba8f9dda9..62fc5e7a9acf 100644 --- a/net/ceph/crypto.c +++ b/net/ceph/crypto.c @@ -476,7 +476,6 @@ struct key_type key_type_ceph = { .preparse = ceph_key_preparse, .free_preparse = ceph_key_free_preparse, .instantiate = generic_key_instantiate, - .match = user_match, .destroy = ceph_key_destroy, }; diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index 92df6e508ae7..a07b9ba7e0b7 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -176,9 +176,8 @@ static void dns_resolver_free_preparse(struct key_preparsed_payload *prep) * The domain name may be a simple name or an absolute domain name (which * should end with a period). The domain name is case-independent. */ -static int -dns_resolver_match(const struct key *key, - const struct key_match_data *match_data) +static int dns_resolver_cmp(const struct key *key, + const struct key_match_data *match_data) { int slen, dlen, ret = 0; const char *src = key->description, *dsp = match_data->raw_data; @@ -209,6 +208,16 @@ no_match: return ret; } +/* + * Preparse the match criterion. + */ +static int dns_resolver_match_preparse(struct key_match_data *match_data) +{ + match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; + match_data->cmp = dns_resolver_cmp; + return 0; +} + /* * Describe a DNS key */ @@ -243,7 +252,7 @@ struct key_type key_type_dns_resolver = { .preparse = dns_resolver_preparse, .free_preparse = dns_resolver_free_preparse, .instantiate = generic_key_instantiate, - .match = dns_resolver_match, + .match_preparse = dns_resolver_match_preparse, .revoke = user_revoke, .destroy = user_destroy, .describe = dns_resolver_describe, diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c index 3907add75932..10c6cb694b43 100644 --- a/net/rxrpc/ar-key.c +++ b/net/rxrpc/ar-key.c @@ -44,7 +44,6 @@ struct key_type key_type_rxrpc = { .preparse = rxrpc_preparse, .free_preparse = rxrpc_free_preparse, .instantiate = generic_key_instantiate, - .match = user_match, .destroy = rxrpc_destroy, .describe = rxrpc_describe, .read = rxrpc_read, @@ -61,7 +60,6 @@ struct key_type key_type_rxrpc_s = { .preparse = rxrpc_preparse_s, .free_preparse = rxrpc_free_preparse_s, .instantiate = generic_key_instantiate, - .match = user_match, .destroy = rxrpc_destroy_s, .describe = rxrpc_describe, }; diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 4045c13a761a..b6adb94f6d52 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -36,7 +36,6 @@ struct key_type key_type_big_key = { .preparse = big_key_preparse, .free_preparse = big_key_free_preparse, .instantiate = generic_key_instantiate, - .match = user_match, .revoke = big_key_revoke, .destroy = big_key_destroy, .describe = big_key_describe, diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 5fe443d120af..db9675db1026 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -970,7 +970,6 @@ struct key_type key_type_encrypted = { .name = "encrypted", .instantiate = encrypted_instantiate, .update = encrypted_update, - .match = user_match, .destroy = encrypted_destroy, .describe = user_describe, .read = encrypted_read, diff --git a/security/keys/internal.h b/security/keys/internal.h index b47cc532be1e..e66a16cb63e1 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -127,6 +127,8 @@ struct keyring_search_context { struct timespec now; }; +extern int key_default_cmp(const struct key *key, + const struct key_match_data *match_data); extern key_ref_t keyring_search_aux(key_ref_t keyring_ref, struct keyring_search_context *ctx); diff --git a/security/keys/key.c b/security/keys/key.c index b90a68c4e2c4..8c0092ca0443 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -799,7 +799,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, } key_ref = ERR_PTR(-EINVAL); - if (!index_key.type->match || !index_key.type->instantiate || + if (!index_key.type->instantiate || (!index_key.description && !index_key.type->preparse)) goto error_put_type; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 10f0a5f2d362..253c9a0eb092 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -89,7 +89,6 @@ struct key_type key_type_keyring = { .preparse = keyring_preparse, .free_preparse = keyring_free_preparse, .instantiate = keyring_instantiate, - .match = user_match, .revoke = keyring_revoke, .destroy = keyring_destroy, .describe = keyring_describe, @@ -511,6 +510,15 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, } EXPORT_SYMBOL(keyring_alloc); +/* + * By default, we keys found by getting an exact match on their descriptions. + */ +int key_default_cmp(const struct key *key, + const struct key_match_data *match_data) +{ + return strcmp(key->description, match_data->raw_data) == 0; +} + /* * Iteration function to consider each key found. */ @@ -884,7 +892,7 @@ key_ref_t keyring_search(key_ref_t keyring, .index_key.type = type, .index_key.description = description, .cred = current_cred(), - .match_data.cmp = type->match, + .match_data.cmp = key_default_cmp, .match_data.raw_data = description, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, .flags = KEYRING_SEARCH_DO_STATE_CHECK, @@ -892,9 +900,6 @@ key_ref_t keyring_search(key_ref_t keyring, key_ref_t key; int ret; - if (!ctx.match_data.cmp) - return ERR_PTR(-ENOKEY); - if (type->match_preparse) { ret = type->match_preparse(&ctx.match_data); if (ret < 0) diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 408523e5e2e2..dc6ed32b7844 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -531,7 +531,7 @@ struct key *request_key_and_link(struct key_type *type, .index_key.type = type, .index_key.description = description, .cred = current_cred(), - .match_data.cmp = type->match, + .match_data.cmp = key_default_cmp, .match_data.raw_data = description, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, }; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 9ae02819cc06..6639e2cb8853 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -246,7 +246,7 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id) .index_key.type = &key_type_request_key_auth, .index_key.description = description, .cred = current_cred(), - .match_data.cmp = user_match, + .match_data.cmp = key_default_cmp, .match_data.raw_data = description, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, }; diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 6b804aa4529a..c0594cb07ada 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -1096,7 +1096,6 @@ struct key_type key_type_trusted = { .name = "trusted", .instantiate = trusted_instantiate, .update = trusted_update, - .match = user_match, .destroy = trusted_destroy, .describe = user_describe, .read = trusted_read, diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index cd7e726e8646..36b47bbd3d8c 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -30,7 +30,6 @@ struct key_type key_type_user = { .free_preparse = user_free_preparse, .instantiate = generic_key_instantiate, .update = user_update, - .match = user_match, .revoke = user_revoke, .destroy = user_destroy, .describe = user_describe, @@ -51,7 +50,6 @@ struct key_type key_type_logon = { .free_preparse = user_free_preparse, .instantiate = generic_key_instantiate, .update = user_update, - .match = user_match, .revoke = user_revoke, .destroy = user_destroy, .describe = user_describe, @@ -136,16 +134,6 @@ error: EXPORT_SYMBOL_GPL(user_update); -/* - * match users on their name - */ -int user_match(const struct key *key, const struct key_match_data *match_data) -{ - return strcmp(key->description, match_data->raw_data) == 0; -} - -EXPORT_SYMBOL_GPL(user_match); - /* * dispose of the links from a revoked keyring * - called with the key sem write-locked -- cgit v1.2.3 From 0c903ab64feb0fe83eac9f67a06e2f5b9508de16 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:08 +0100 Subject: KEYS: Make the key matching functions return bool Make the key matching functions pointed to by key_match_data::cmp return bool rather than int. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/asymmetric_type.c | 4 ++-- include/linux/key-type.h | 10 ++++++---- net/dns_resolver/dns_key.c | 4 ++-- security/keys/internal.h | 8 ++++---- security/keys/keyring.c | 4 ++-- security/keys/process_keys.c | 4 ++-- 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 7c0498968975..7755f918e8d9 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -59,8 +59,8 @@ EXPORT_SYMBOL_GPL(asymmetric_keyid_match); * "id:" - request a key matching the ID * ":" - request a key of a subtype */ -static int asymmetric_key_cmp(const struct key *key, - const struct key_match_data *match_data) +static bool asymmetric_key_cmp(const struct key *key, + const struct key_match_data *match_data) { const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); const char *description = match_data->raw_data; diff --git a/include/linux/key-type.h b/include/linux/key-type.h index c14816bd3b44..ff9f1d394235 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -56,10 +56,12 @@ typedef int (*request_key_actor_t)(struct key_construction *key, * Preparsed matching criterion. */ struct key_match_data { - /* Comparison function, defaults to type->match, but can be replaced by - * type->match_preparse(). */ - int (*cmp)(const struct key *key, - const struct key_match_data *match_data); + /* Comparison function, defaults to exact description match, but can be + * overridden by type->match_preparse(). Should return true if a match + * is found and false if not. + */ + bool (*cmp)(const struct key *key, + const struct key_match_data *match_data); const void *raw_data; /* Raw match data */ void *preparsed; /* For ->match_preparse() to stash stuff */ diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index a07b9ba7e0b7..31cd4fd75486 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -176,8 +176,8 @@ static void dns_resolver_free_preparse(struct key_preparsed_payload *prep) * The domain name may be a simple name or an absolute domain name (which * should end with a period). The domain name is case-independent. */ -static int dns_resolver_cmp(const struct key *key, - const struct key_match_data *match_data) +static bool dns_resolver_cmp(const struct key *key, + const struct key_match_data *match_data) { int slen, dlen, ret = 0; const char *src = key->description, *dsp = match_data->raw_data; diff --git a/security/keys/internal.h b/security/keys/internal.h index e66a16cb63e1..b8960c4959a5 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -127,8 +127,8 @@ struct keyring_search_context { struct timespec now; }; -extern int key_default_cmp(const struct key *key, - const struct key_match_data *match_data); +extern bool key_default_cmp(const struct key *key, + const struct key_match_data *match_data); extern key_ref_t keyring_search_aux(key_ref_t keyring_ref, struct keyring_search_context *ctx); @@ -150,8 +150,8 @@ extern struct key *request_key_and_link(struct key_type *type, struct key *dest_keyring, unsigned long flags); -extern int lookup_user_key_possessed(const struct key *key, - const struct key_match_data *match_data); +extern bool lookup_user_key_possessed(const struct key *key, + const struct key_match_data *match_data); extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, key_perm_t perm); #define KEY_LOOKUP_CREATE 0x01 diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 253c9a0eb092..8177010174f7 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -513,8 +513,8 @@ EXPORT_SYMBOL(keyring_alloc); /* * By default, we keys found by getting an exact match on their descriptions. */ -int key_default_cmp(const struct key *key, - const struct key_match_data *match_data) +bool key_default_cmp(const struct key *key, + const struct key_match_data *match_data) { return strcmp(key->description, match_data->raw_data) == 0; } diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 08bd533d014f..bd536cb221e2 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -489,8 +489,8 @@ found: /* * See if the key we're looking at is the target key. */ -int lookup_user_key_possessed(const struct key *key, - const struct key_match_data *match_data) +bool lookup_user_key_possessed(const struct key *key, + const struct key_match_data *match_data) { return key == match_data->raw_data; } -- cgit v1.2.3 From f93b3cc7b1e6f16aedd745a8edba64355383184c Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:09 +0100 Subject: KEYS: Update the keyrings documentation for match changes Signed-off-by: David Howells Acked-by: Vivek Goyal --- Documentation/security/keys.txt | 65 ++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 8727c194ca16..821c936e1a63 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -888,11 +888,11 @@ payload contents" for more information. const char *callout_info); This is used to request a key or keyring with a description that matches - the description specified according to the key type's match function. This - permits approximate matching to occur. If callout_string is not NULL, then - /sbin/request-key will be invoked in an attempt to obtain the key from - userspace. In that case, callout_string will be passed as an argument to - the program. + the description specified according to the key type's match_preparse() + method. This permits approximate matching to occur. If callout_string is + not NULL, then /sbin/request-key will be invoked in an attempt to obtain + the key from userspace. In that case, callout_string will be passed as an + argument to the program. Should the function fail error ENOKEY, EKEYEXPIRED or EKEYREVOKED will be returned. @@ -1170,7 +1170,7 @@ The structure has a number of fields, some of which are mandatory: The method should return 0 if successful or a negative error code otherwise. - + (*) void (*free_preparse)(struct key_preparsed_payload *prep); This method is only required if the preparse() method is provided, @@ -1225,16 +1225,55 @@ The structure has a number of fields, some of which are mandatory: It is safe to sleep in this method. - (*) int (*match)(const struct key *key, const void *desc); + (*) int (*match_preparse)(struct key_match_data *match_data); + + This method is optional. It is called when a key search is about to be + performed. It is given the following structure: - This method is called to match a key against a description. It should - return non-zero if the two match, zero if they don't. + struct key_match_data { + bool (*cmp)(const struct key *key, + const struct key_match_data *match_data); + const void *raw_data; + void *preparsed; + unsigned lookup_type; + }; - This method should not need to lock the key in any way. The type and - description can be considered invariant, and the payload should not be - accessed (the key may not yet be instantiated). + On entry, raw_data will be pointing to the criteria to be used in matching + a key by the caller and should not be modified. (*cmp)() will be pointing + to the default matcher function (which does an exact description match + against raw_data) and lookup_type will be set to indicate a direct lookup. - It is not safe to sleep in this method; the caller may hold spinlocks. + The following lookup_type values are available: + + [*] KEYRING_SEARCH_LOOKUP_DIRECT - A direct lookup hashes the type and + description to narrow down the search to a small number of keys. + + [*] KEYRING_SEARCH_LOOKUP_ITERATE - An iterative lookup walks all the + keys in the keyring until one is matched. This must be used for any + search that's not doing a simple direct match on the key description. + + The method may set cmp to point to a function of its choice that does some + other form of match, may set lookup_type to KEYRING_SEARCH_LOOKUP_ITERATE + and may attach something to the preparsed pointer for use by (*cmp)(). + (*cmp)() should return true if a key matches and false otherwise. + + If preparsed is set, it may be necessary to use the match_free() method to + clean it up. + + The method should return 0 if successful or a negative error code + otherwise. + + It is permitted to sleep in this method, but (*cmp)() may not sleep as + locks will be held over it. + + If match_preparse() is not provided, keys of this type will be matched + exactly by their description. + + + (*) void (*match_free)(struct key_match_data *match_data); + + This method is optional. If given, it called to clean up + match_data->preparsed after a successful call to match_preparse(). (*) void (*revoke)(struct key *key); -- cgit v1.2.3 From 7901c1a8effbe5f89673bfc09d6e37b8f334f1a7 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:11 +0100 Subject: KEYS: Implement binary asymmetric key ID handling Implement the first step in using binary key IDs for asymmetric keys rather than hex string keys. The previously added match data preparsing will be able to convert hex criterion strings into binary which can then be compared more rapidly. Further, we actually want more then one ID string per public key. The problem is that X.509 certs refer to other X.509 certs by matching Issuer + AuthKeyId to Subject + SubjKeyId, but PKCS#7 messages match against X.509 Issuer + SerialNumber. This patch just provides facilities for a later patch to make use of. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/asymmetric_keys.h | 4 ++ crypto/asymmetric_keys/asymmetric_type.c | 89 ++++++++++++++++++++++++++++++++ include/keys/asymmetric-type.h | 38 ++++++++++++++ 3 files changed, 131 insertions(+) diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h index a63c551c6557..917be6b985e7 100644 --- a/crypto/asymmetric_keys/asymmetric_keys.h +++ b/crypto/asymmetric_keys/asymmetric_keys.h @@ -10,6 +10,10 @@ */ int asymmetric_keyid_match(const char *kid, const char *id); +extern bool asymmetric_match_key_ids(const struct asymmetric_key_ids *kids, + const struct asymmetric_key_id *match_id); + +extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id); static inline const char *asymmetric_key_id(const struct key *key) { diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 7755f918e8d9..92bfc438dd1d 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "asymmetric_keys.h" MODULE_LICENSE("GPL"); @@ -22,6 +23,94 @@ MODULE_LICENSE("GPL"); static LIST_HEAD(asymmetric_key_parsers); static DECLARE_RWSEM(asymmetric_key_parsers_sem); +/** + * asymmetric_key_generate_id: Construct an asymmetric key ID + * @val_1: First binary blob + * @len_1: Length of first binary blob + * @val_2: Second binary blob + * @len_2: Length of second binary blob + * + * Construct an asymmetric key ID from a pair of binary blobs. + */ +struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1, + size_t len_1, + const void *val_2, + size_t len_2) +{ + struct asymmetric_key_id *kid; + + kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2, + GFP_KERNEL); + if (!kid) + return ERR_PTR(-ENOMEM); + kid->len = len_1 + len_2; + memcpy(kid->data, val_1, len_1); + memcpy(kid->data + len_1, val_2, len_2); + return kid; +} +EXPORT_SYMBOL_GPL(asymmetric_key_generate_id); + +/** + * asymmetric_key_id_same - Return true if two asymmetric keys IDs are the same. + * @kid_1, @kid_2: The key IDs to compare + */ +bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1, + const struct asymmetric_key_id *kid2) +{ + if (!kid1 || !kid2) + return false; + if (kid1->len != kid2->len) + return false; + return memcmp(kid1->data, kid2->data, kid1->len) == 0; +} +EXPORT_SYMBOL_GPL(asymmetric_key_id_same); + +/** + * asymmetric_match_key_ids - Search asymmetric key IDs + * @kids: The list of key IDs to check + * @match_id: The key ID we're looking for + */ +bool asymmetric_match_key_ids(const struct asymmetric_key_ids *kids, + const struct asymmetric_key_id *match_id) +{ + if (!kids || !match_id) + return false; + if (asymmetric_key_id_same(kids->id[0], match_id)) + return true; + if (asymmetric_key_id_same(kids->id[1], match_id)) + return true; + return false; +} +EXPORT_SYMBOL_GPL(asymmetric_match_key_ids); + +/** + * asymmetric_key_hex_to_key_id - Convert a hex string into a key ID. + * @id: The ID as a hex string. + */ +struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id) +{ + struct asymmetric_key_id *match_id; + const char *p; + ptrdiff_t hexlen; + + if (!*id) + return ERR_PTR(-EINVAL); + for (p = id; *p; p++) + if (!isxdigit(*p)) + return ERR_PTR(-EINVAL); + hexlen = p - id; + if (hexlen & 1) + return ERR_PTR(-EINVAL); + + match_id = kmalloc(sizeof(struct asymmetric_key_id) + hexlen / 2, + GFP_KERNEL); + if (!match_id) + return ERR_PTR(-ENOMEM); + match_id->len = hexlen / 2; + (void)hex2bin(match_id->data, id, hexlen / 2); + return match_id; +} + /* * Match asymmetric key id with partial match * @id: key id to match in a form "id:" diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h index 7dd473496180..044ab0d3aa45 100644 --- a/include/keys/asymmetric-type.h +++ b/include/keys/asymmetric-type.h @@ -18,6 +18,44 @@ extern struct key_type key_type_asymmetric; +/* + * Identifiers for an asymmetric key ID. We have three ways of looking up a + * key derived from an X.509 certificate: + * + * (1) Serial Number & Issuer. Non-optional. This is the only valid way to + * map a PKCS#7 signature to an X.509 certificate. + * + * (2) Issuer & Subject Unique IDs. Optional. These were the original way to + * match X.509 certificates, but have fallen into disuse in favour of (3). + * + * (3) Auth & Subject Key Identifiers. Optional. SKIDs are only provided on + * CA keys that are intended to sign other keys, so don't appear in end + * user certificates unless forced. + * + * We could also support an PGP key identifier, which is just a SHA1 sum of the + * public key and certain parameters, but since we don't support PGP keys at + * the moment, we shall ignore those. + * + * What we actually do is provide a place where binary identifiers can be + * stashed and then compare against them when checking for an id match. + */ +struct asymmetric_key_id { + unsigned short len; + unsigned char data[]; +}; + +struct asymmetric_key_ids { + void *id[2]; +}; + +extern bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1, + const struct asymmetric_key_id *kid2); + +extern struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1, + size_t len_1, + const void *val_2, + size_t len_2); + /* * The payload is at the discretion of the subtype. */ -- cgit v1.2.3 From 46963b774d441c833afc1535f6d84b3df2a94204 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:13 +0100 Subject: KEYS: Overhaul key identification when searching for asymmetric keys Make use of the new match string preparsing to overhaul key identification when searching for asymmetric keys. The following changes are made: (1) Use the previously created asymmetric_key_id struct to hold the following key IDs derived from the X.509 certificate or PKCS#7 message: id: serial number + issuer skid: subjKeyId + subject authority: authKeyId + issuer (2) Replace the hex fingerprint attached to key->type_data[1] with an asymmetric_key_ids struct containing the id and the skid (if present). (3) Make the asymmetric_type match data preparse select one of two searches: (a) An iterative search for the key ID given if prefixed with "id:". The prefix is expected to be followed by a hex string giving the ID to search for. The criterion key ID is checked against all key IDs recorded on the key. (b) A direct search if the key ID is not prefixed with "id:". This will look for an exact match on the key description. (4) Make x509_request_asymmetric_key() take a key ID. This is then converted into "id:" and passed into keyring_search() where match preparsing will turn it back into a binary ID. (5) X.509 certificate verification then takes the authority key ID and looks up a key that matches it to find the public key for the certificate signature. (6) PKCS#7 certificate verification then takes the id key ID and looks up a key that matches it to find the public key for the signed information block signature. Additional changes: (1) Multiple subjKeyId and authKeyId values on an X.509 certificate cause the cert to be rejected with -EBADMSG. (2) The 'fingerprint' ID is gone. This was primarily intended to convey PGP public key fingerprints. If PGP is supported in future, this should generate a key ID that carries the fingerprint. (3) Th ca_keyid= kernel command line option is now converted to a key ID and used to match the authority key ID. Possibly this should only match the actual authKeyId part and not the issuer as well. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/asymmetric_keys.h | 4 +- crypto/asymmetric_keys/asymmetric_type.c | 133 +++++++++++++----------------- crypto/asymmetric_keys/pkcs7_parser.c | 38 ++++++--- crypto/asymmetric_keys/pkcs7_parser.h | 5 +- crypto/asymmetric_keys/pkcs7_trust.c | 6 +- crypto/asymmetric_keys/pkcs7_verify.c | 44 +++++----- crypto/asymmetric_keys/x509_cert_parser.c | 55 +++++++----- crypto/asymmetric_keys/x509_parser.h | 5 +- crypto/asymmetric_keys/x509_public_key.c | 89 +++++++++++--------- include/crypto/public_key.h | 5 +- 10 files changed, 198 insertions(+), 186 deletions(-) diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h index 917be6b985e7..fd21ac28e0a0 100644 --- a/crypto/asymmetric_keys/asymmetric_keys.h +++ b/crypto/asymmetric_keys/asymmetric_keys.h @@ -9,13 +9,13 @@ * 2 of the Licence, or (at your option) any later version. */ -int asymmetric_keyid_match(const char *kid, const char *id); extern bool asymmetric_match_key_ids(const struct asymmetric_key_ids *kids, const struct asymmetric_key_id *match_id); extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id); -static inline const char *asymmetric_key_id(const struct key *key) +static inline +const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key) { return key->type_data.p[1]; } diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 92bfc438dd1d..718e779a010e 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -112,76 +112,15 @@ struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id) } /* - * Match asymmetric key id with partial match - * @id: key id to match in a form "id:" - */ -int asymmetric_keyid_match(const char *kid, const char *id) -{ - size_t idlen, kidlen; - - if (!kid || !id) - return 0; - - /* make it possible to use id as in the request: "id:" */ - if (strncmp(id, "id:", 3) == 0) - id += 3; - - /* Anything after here requires a partial match on the ID string */ - idlen = strlen(id); - kidlen = strlen(kid); - if (idlen > kidlen) - return 0; - - kid += kidlen - idlen; - if (strcasecmp(id, kid) != 0) - return 0; - - return 1; -} -EXPORT_SYMBOL_GPL(asymmetric_keyid_match); - -/* - * Match asymmetric keys on (part of) their name - * We have some shorthand methods for matching keys. We allow: - * - * "" - request a key by description - * "id:" - request a key matching the ID - * ":" - request a key of a subtype + * Match asymmetric keys by ID. */ static bool asymmetric_key_cmp(const struct key *key, const struct key_match_data *match_data) { - const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); - const char *description = match_data->raw_data; - const char *spec = description; - const char *id; - ptrdiff_t speclen; - - if (!subtype || !spec || !*spec) - return 0; - - /* See if the full key description matches as is */ - if (key->description && strcmp(key->description, description) == 0) - return 1; - - /* All tests from here on break the criterion description into a - * specifier, a colon and then an identifier. - */ - id = strchr(spec, ':'); - if (!id) - return 0; - - speclen = id - spec; - id++; - - if (speclen == 2 && memcmp(spec, "id", 2) == 0) - return asymmetric_keyid_match(asymmetric_key_id(key), id); + const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); + const struct asymmetric_key_id *match_id = match_data->preparsed; - if (speclen == subtype->name_len && - memcmp(spec, subtype->name, speclen) == 0) - return 1; - - return 0; + return asymmetric_match_key_ids(kids, match_id); } /* @@ -198,8 +137,30 @@ static bool asymmetric_key_cmp(const struct key *key, */ static int asymmetric_key_match_preparse(struct key_match_data *match_data) { - match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; + struct asymmetric_key_id *match_id; + const char *spec = match_data->raw_data; + const char *id; + + if (!spec || !*spec) + return -EINVAL; + if (spec[0] == 'i' && + spec[1] == 'd' && + spec[2] == ':') { + id = spec + 3; + } else { + goto default_match; + } + + match_id = asymmetric_key_hex_to_key_id(id); + if (!match_id) + return -ENOMEM; + + match_data->preparsed = match_id; match_data->cmp = asymmetric_key_cmp; + match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; + return 0; + +default_match: return 0; } @@ -208,6 +169,7 @@ static int asymmetric_key_match_preparse(struct key_match_data *match_data) */ static void asymmetric_key_match_free(struct key_match_data *match_data) { + kfree(match_data->preparsed); } /* @@ -216,8 +178,10 @@ static void asymmetric_key_match_free(struct key_match_data *match_data) static void asymmetric_key_describe(const struct key *key, struct seq_file *m) { const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); - const char *kid = asymmetric_key_id(key); - size_t n; + const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); + const struct asymmetric_key_id *kid; + const unsigned char *p; + int n; seq_puts(m, key->description); @@ -225,13 +189,16 @@ static void asymmetric_key_describe(const struct key *key, struct seq_file *m) seq_puts(m, ": "); subtype->describe(key, m); - if (kid) { + if (kids && kids->id[0]) { + kid = kids->id[0]; seq_putc(m, ' '); - n = strlen(kid); - if (n <= 8) - seq_puts(m, kid); - else - seq_puts(m, kid + n - 8); + n = kid->len; + p = kid->data; + if (n > 8) { + p += n - 8; + n = 8; + } + seq_printf(m, "%*phN", n, p); } seq_puts(m, " ["); @@ -282,6 +249,7 @@ static int asymmetric_key_preparse(struct key_preparsed_payload *prep) static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) { struct asymmetric_key_subtype *subtype = prep->type_data[0]; + struct asymmetric_key_ids *kids = prep->type_data[1]; pr_devel("==>%s()\n", __func__); @@ -289,7 +257,11 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) subtype->destroy(prep->payload[0]); module_put(subtype->owner); } - kfree(prep->type_data[1]); + if (kids) { + kfree(kids->id[0]); + kfree(kids->id[1]); + kfree(kids); + } kfree(prep->description); } @@ -299,13 +271,20 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) static void asymmetric_key_destroy(struct key *key) { struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); + struct asymmetric_key_ids *kids = key->type_data.p[1]; + if (subtype) { subtype->destroy(key->payload.data); module_put(subtype->owner); key->type_data.p[0] = NULL; } - kfree(key->type_data.p[1]); - key->type_data.p[1] = NULL; + + if (kids) { + kfree(kids->id[0]); + kfree(kids->id[1]); + kfree(kids); + key->type_data.p[1] = NULL; + } } struct key_type key_type_asymmetric = { diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 1e9861da7ee4..3bd5a1e4c493 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -29,6 +29,10 @@ struct pkcs7_parse_context { enum OID last_oid; /* Last OID encountered */ unsigned x509_index; unsigned sinfo_index; + const void *raw_serial; + unsigned raw_serial_size; + unsigned raw_issuer_size; + const void *raw_issuer; }; /* @@ -39,6 +43,7 @@ static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo) if (sinfo) { mpi_free(sinfo->sig.mpi[0]); kfree(sinfo->sig.digest); + kfree(sinfo->signing_cert_id); kfree(sinfo); } } @@ -251,10 +256,10 @@ int pkcs7_extract_cert(void *context, size_t hdrlen, if (IS_ERR(x509)) return PTR_ERR(x509); - pr_debug("Got cert for %s\n", x509->subject); - pr_debug("- fingerprint %s\n", x509->fingerprint); - x509->index = ++ctx->x509_index; + pr_debug("Got cert %u for %s\n", x509->index, x509->subject); + pr_debug("- fingerprint %*phN\n", x509->id->len, x509->id->data); + *ctx->ppcerts = x509; ctx->ppcerts = &x509->next; return 0; @@ -343,8 +348,8 @@ int pkcs7_sig_note_serial(void *context, size_t hdrlen, const void *value, size_t vlen) { struct pkcs7_parse_context *ctx = context; - ctx->sinfo->raw_serial = value; - ctx->sinfo->raw_serial_size = vlen; + ctx->raw_serial = value; + ctx->raw_serial_size = vlen; return 0; } @@ -356,8 +361,8 @@ int pkcs7_sig_note_issuer(void *context, size_t hdrlen, const void *value, size_t vlen) { struct pkcs7_parse_context *ctx = context; - ctx->sinfo->raw_issuer = value; - ctx->sinfo->raw_issuer_size = vlen; + ctx->raw_issuer = value; + ctx->raw_issuer_size = vlen; return 0; } @@ -390,10 +395,21 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen, const void *value, size_t vlen) { struct pkcs7_parse_context *ctx = context; - - ctx->sinfo->index = ++ctx->sinfo_index; - *ctx->ppsinfo = ctx->sinfo; - ctx->ppsinfo = &ctx->sinfo->next; + struct pkcs7_signed_info *sinfo = ctx->sinfo; + struct asymmetric_key_id *kid; + + /* Generate cert issuer + serial number key ID */ + kid = asymmetric_key_generate_id(ctx->raw_serial, + ctx->raw_serial_size, + ctx->raw_issuer, + ctx->raw_issuer_size); + if (IS_ERR(kid)) + return PTR_ERR(kid); + + sinfo->signing_cert_id = kid; + sinfo->index = ++ctx->sinfo_index; + *ctx->ppsinfo = sinfo; + ctx->ppsinfo = &sinfo->next; ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); if (!ctx->sinfo) return -ENOMEM; diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h index d25f4d15370f..91949f92bc72 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.h +++ b/crypto/asymmetric_keys/pkcs7_parser.h @@ -33,10 +33,7 @@ struct pkcs7_signed_info { const void *authattrs; /* Issuing cert serial number and issuer's name */ - const void *raw_serial; - unsigned raw_serial_size; - unsigned raw_issuer_size; - const void *raw_issuer; + struct asymmetric_key_id *signing_cert_id; /* Message signature. * diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index fad888ea4fad..09197e50fa82 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -49,8 +49,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, /* Look to see if this certificate is present in the trusted * keys. */ - key = x509_request_asymmetric_key(trust_keyring, x509->subject, - x509->fingerprint); + key = x509_request_asymmetric_key(trust_keyring, x509->id); if (!IS_ERR(key)) /* One of the X.509 certificates in the PKCS#7 message * is apparently the same as one we already trust. @@ -82,8 +81,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, return -ENOKEY; } - key = x509_request_asymmetric_key(trust_keyring, last->issuer, - last->authority); + key = x509_request_asymmetric_key(trust_keyring, last->authority); if (IS_ERR(key)) return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY; x509 = last; diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index c62cf8006e1f..57e90fa17f2b 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -131,8 +131,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, struct x509_certificate *x509; unsigned certix = 1; - kenter("%u,%u,%u", - sinfo->index, sinfo->raw_serial_size, sinfo->raw_issuer_size); + kenter("%u", sinfo->index); for (x509 = pkcs7->certs; x509; x509 = x509->next, certix++) { /* I'm _assuming_ that the generator of the PKCS#7 message will @@ -140,21 +139,11 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, * PKCS#7 message - but I can't be 100% sure of that. It's * possible this will need element-by-element comparison. */ - if (x509->raw_serial_size != sinfo->raw_serial_size || - memcmp(x509->raw_serial, sinfo->raw_serial, - sinfo->raw_serial_size) != 0) + if (!asymmetric_key_id_same(x509->id, sinfo->signing_cert_id)) continue; pr_devel("Sig %u: Found cert serial match X.509[%u]\n", sinfo->index, certix); - if (x509->raw_issuer_size != sinfo->raw_issuer_size || - memcmp(x509->raw_issuer, sinfo->raw_issuer, - sinfo->raw_issuer_size) != 0) { - pr_warn("Sig %u: X.509 subject and PKCS#7 issuer don't match\n", - sinfo->index); - continue; - } - if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) { pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n", sinfo->index); @@ -164,8 +153,10 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, sinfo->signer = x509; return 0; } + pr_warn("Sig %u: Issuing X.509 cert not found (#%*ph)\n", - sinfo->index, sinfo->raw_serial_size, sinfo->raw_serial); + sinfo->index, + sinfo->signing_cert_id->len, sinfo->signing_cert_id->data); return -ENOKEY; } @@ -184,7 +175,9 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, p->seen = false; for (;;) { - pr_debug("verify %s: %s\n", x509->subject, x509->fingerprint); + pr_debug("verify %s: %*phN\n", + x509->subject, + x509->raw_serial_size, x509->raw_serial); x509->seen = true; ret = x509_get_sig_params(x509); if (ret < 0) @@ -192,7 +185,8 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, pr_debug("- issuer %s\n", x509->issuer); if (x509->authority) - pr_debug("- authkeyid %s\n", x509->authority); + pr_debug("- authkeyid %*phN\n", + x509->authority->len, x509->authority->data); if (!x509->authority || strcmp(x509->subject, x509->issuer) == 0) { @@ -218,13 +212,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, /* Look through the X.509 certificates in the PKCS#7 message's * list to see if the next one is there. */ - pr_debug("- want %s\n", x509->authority); + pr_debug("- want %*phN\n", + x509->authority->len, x509->authority->data); for (p = pkcs7->certs; p; p = p->next) { - pr_debug("- cmp [%u] %s\n", p->index, p->fingerprint); - if (p->raw_subject_size == x509->raw_issuer_size && - strcmp(p->fingerprint, x509->authority) == 0 && - memcmp(p->raw_subject, x509->raw_issuer, - x509->raw_issuer_size) == 0) + if (!p->skid) + continue; + pr_debug("- cmp [%u] %*phN\n", + p->index, p->skid->len, p->skid->data); + if (asymmetric_key_id_same(p->skid, x509->authority)) goto found_issuer; } @@ -233,7 +228,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, return 0; found_issuer: - pr_debug("- issuer %s\n", p->subject); + pr_debug("- subject %s\n", p->subject); if (p->seen) { pr_warn("Sig %u: X.509 chain contains loop\n", sinfo->index); @@ -304,7 +299,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7) ret = x509_get_sig_params(x509); if (ret < 0) return ret; - pr_debug("X.509[%u] %s\n", n, x509->authority); + pr_debug("X.509[%u] %*phN\n", + n, x509->authority->len, x509->authority->data); } for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index ac72348c186a..96151b2b91a2 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -46,7 +46,8 @@ void x509_free_certificate(struct x509_certificate *cert) public_key_destroy(cert->pub); kfree(cert->issuer); kfree(cert->subject); - kfree(cert->fingerprint); + kfree(cert->id); + kfree(cert->skid); kfree(cert->authority); kfree(cert->sig.digest); mpi_free(cert->sig.rsa.s); @@ -62,6 +63,7 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) { struct x509_certificate *cert; struct x509_parse_context *ctx; + struct asymmetric_key_id *kid; long ret; ret = -ENOMEM; @@ -89,6 +91,17 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) if (ret < 0) goto error_decode; + /* Generate cert issuer + serial number key ID */ + kid = asymmetric_key_generate_id(cert->raw_serial, + cert->raw_serial_size, + cert->raw_issuer, + cert->raw_issuer_size); + if (IS_ERR(kid)) { + ret = PTR_ERR(kid); + goto error_decode; + } + cert->id = kid; + kfree(ctx); return cert; @@ -407,36 +420,34 @@ int x509_process_extension(void *context, size_t hdrlen, const void *value, size_t vlen) { struct x509_parse_context *ctx = context; + struct asymmetric_key_id *kid; const unsigned char *v = value; - char *f; int i; pr_debug("Extension: %u\n", ctx->last_oid); if (ctx->last_oid == OID_subjectKeyIdentifier) { /* Get hold of the key fingerprint */ - if (vlen < 3) + if (ctx->cert->skid || vlen < 3) return -EBADMSG; if (v[0] != ASN1_OTS || v[1] != vlen - 2) return -EBADMSG; v += 2; vlen -= 2; - f = kmalloc(vlen * 2 + 1, GFP_KERNEL); - if (!f) - return -ENOMEM; - for (i = 0; i < vlen; i++) - sprintf(f + i * 2, "%02x", v[i]); - pr_debug("fingerprint %s\n", f); - ctx->cert->fingerprint = f; + kid = asymmetric_key_generate_id(v, vlen, + ctx->cert->raw_subject, + ctx->cert->raw_subject_size); + if (IS_ERR(kid)) + return PTR_ERR(kid); + ctx->cert->skid = kid; + pr_debug("subjkeyid %*phN\n", kid->len, kid->data); return 0; } if (ctx->last_oid == OID_authorityKeyIdentifier) { - size_t key_len; - /* Get hold of the CA key fingerprint */ - if (vlen < 5) + if (ctx->cert->authority || vlen < 5) return -EBADMSG; /* Authority Key Identifier must be a Constructed SEQUENCE */ @@ -454,7 +465,7 @@ int x509_process_extension(void *context, size_t hdrlen, v[3] > vlen - 4) return -EBADMSG; - key_len = v[3]; + vlen = v[3]; v += 4; } else { /* Long Form length */ @@ -476,17 +487,17 @@ int x509_process_extension(void *context, size_t hdrlen, v[sub + 1] > vlen - 4 - sub) return -EBADMSG; - key_len = v[sub + 1]; + vlen = v[sub + 1]; v += (sub + 2); } - f = kmalloc(key_len * 2 + 1, GFP_KERNEL); - if (!f) - return -ENOMEM; - for (i = 0; i < key_len; i++) - sprintf(f + i * 2, "%02x", v[i]); - pr_debug("authority %s\n", f); - ctx->cert->authority = f; + kid = asymmetric_key_generate_id(v, vlen, + ctx->cert->raw_issuer, + ctx->cert->raw_issuer_size); + if (IS_ERR(kid)) + return PTR_ERR(kid); + pr_debug("authkeyid %*phN\n", kid->len, kid->data); + ctx->cert->authority = kid; return 0; } diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index 1b76f207c1f3..0e8d59b010fb 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -19,8 +19,9 @@ struct x509_certificate { struct public_key_signature sig; /* Signature parameters */ char *issuer; /* Name of certificate issuer */ char *subject; /* Name of certificate subject */ - char *fingerprint; /* Key fingerprint as hex */ - char *authority; /* Authority key fingerprint as hex */ + struct asymmetric_key_id *id; /* Issuer + serial number */ + struct asymmetric_key_id *skid; /* Subject key identifier */ + struct asymmetric_key_id *authority; /* Authority key identifier */ struct tm valid_from; struct tm valid_to; const void *tbs; /* Signed data */ diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index f3d62307e6ee..c60905c3f4d2 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -25,7 +25,7 @@ #include "x509_parser.h" static bool use_builtin_keys; -static char *ca_keyid; +static struct asymmetric_key_id *ca_keyid; #ifndef MODULE static int __init ca_keys_setup(char *str) @@ -33,10 +33,16 @@ static int __init ca_keys_setup(char *str) if (!str) /* default system keyring */ return 1; - if (strncmp(str, "id:", 3) == 0) - ca_keyid = str; /* owner key 'id:xxxxxx' */ - else if (strcmp(str, "builtin") == 0) + if (strncmp(str, "id:", 3) == 0) { + struct asymmetric_key_id *p; + p = asymmetric_key_hex_to_key_id(str); + if (p == ERR_PTR(-EINVAL)) + pr_err("Unparsable hex string in ca_keys\n"); + else if (!IS_ERR(p)) + ca_keyid = p; /* owner key 'id:xxxxxx' */ + } else if (strcmp(str, "builtin") == 0) { use_builtin_keys = true; + } return 1; } @@ -46,31 +52,28 @@ __setup("ca_keys=", ca_keys_setup); /** * x509_request_asymmetric_key - Request a key by X.509 certificate params. * @keyring: The keys to search. - * @subject: The name of the subject to whom the key belongs. - * @key_id: The subject key ID as a hex string. + * @kid: The key ID. * * Find a key in the given keyring by subject name and key ID. These might, * for instance, be the issuer name and the authority key ID of an X.509 * certificate that needs to be verified. */ struct key *x509_request_asymmetric_key(struct key *keyring, - const char *subject, - const char *key_id) + const struct asymmetric_key_id *kid) { key_ref_t key; - size_t subject_len = strlen(subject), key_id_len = strlen(key_id); - char *id; + char *id, *p; - /* Construct an identifier ":". */ - id = kmalloc(subject_len + 2 + key_id_len + 1, GFP_KERNEL); + /* Construct an identifier "id:". */ + p = id = kmalloc(2 + 1 + kid->len * 2 + 1, GFP_KERNEL); if (!id) return ERR_PTR(-ENOMEM); - memcpy(id, subject, subject_len); - id[subject_len + 0] = ':'; - id[subject_len + 1] = ' '; - memcpy(id + subject_len + 2, key_id, key_id_len); - id[subject_len + 2 + key_id_len] = 0; + *p++ = 'i'; + *p++ = 'd'; + *p++ = ':'; + p = bin2hex(p, kid->data, kid->len); + *p = 0; pr_debug("Look up: \"%s\"\n", id); @@ -195,11 +198,10 @@ static int x509_validate_trust(struct x509_certificate *cert, if (!trust_keyring) return -EOPNOTSUPP; - if (ca_keyid && !asymmetric_keyid_match(cert->authority, ca_keyid)) + if (ca_keyid && !asymmetric_key_id_same(cert->authority, ca_keyid)) return -EPERM; - key = x509_request_asymmetric_key(trust_keyring, - cert->issuer, cert->authority); + key = x509_request_asymmetric_key(trust_keyring, cert->authority); if (!IS_ERR(key)) { if (!use_builtin_keys || test_bit(KEY_FLAG_BUILTIN, &key->flags)) @@ -214,9 +216,11 @@ static int x509_validate_trust(struct x509_certificate *cert, */ static int x509_key_preparse(struct key_preparsed_payload *prep) { + struct asymmetric_key_ids *kids; struct x509_certificate *cert; + const char *q; size_t srlen, sulen; - char *desc = NULL; + char *desc = NULL, *p; int ret; cert = x509_cert_parse(prep->data, prep->datalen); @@ -249,19 +253,12 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) pkey_algo_name[cert->sig.pkey_algo], hash_algo_name[cert->sig.pkey_hash_algo]); - if (!cert->fingerprint) { - pr_warn("Cert for '%s' must have a SubjKeyId extension\n", - cert->subject); - ret = -EKEYREJECTED; - goto error_free_cert; - } - cert->pub->algo = pkey_algo[cert->pub->pkey_algo]; cert->pub->id_type = PKEY_ID_X509; /* Check the signature on the key if it appears to be self-signed */ if (!cert->authority || - strcmp(cert->fingerprint, cert->authority) == 0) { + asymmetric_key_id_same(cert->skid, cert->authority)) { ret = x509_check_signature(cert->pub, cert); /* self-signed */ if (ret < 0) goto error_free_cert; @@ -273,31 +270,47 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) /* Propose a description */ sulen = strlen(cert->subject); - srlen = strlen(cert->fingerprint); + srlen = cert->raw_serial_size; + q = cert->raw_serial; + if (srlen > 1 && *q == 0) { + srlen--; + q++; + } + ret = -ENOMEM; - desc = kmalloc(sulen + 2 + srlen + 1, GFP_KERNEL); + desc = kmalloc(sulen + 2 + srlen * 2 + 1, GFP_KERNEL); if (!desc) goto error_free_cert; - memcpy(desc, cert->subject, sulen); - desc[sulen] = ':'; - desc[sulen + 1] = ' '; - memcpy(desc + sulen + 2, cert->fingerprint, srlen); - desc[sulen + 2 + srlen] = 0; + p = memcpy(desc, cert->subject, sulen); + p += sulen; + *p++ = ':'; + *p++ = ' '; + p = bin2hex(p, q, srlen); + *p = 0; + + kids = kmalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL); + if (!kids) + goto error_free_desc; + kids->id[0] = cert->id; + kids->id[1] = cert->skid; /* We're pinning the module by being linked against it */ __module_get(public_key_subtype.owner); prep->type_data[0] = &public_key_subtype; - prep->type_data[1] = cert->fingerprint; + prep->type_data[1] = kids; prep->payload[0] = cert->pub; prep->description = desc; prep->quotalen = 100; /* We've finished with the certificate */ cert->pub = NULL; - cert->fingerprint = NULL; + cert->id = NULL; + cert->skid = NULL; desc = NULL; ret = 0; +error_free_desc: + kfree(desc); error_free_cert: x509_free_certificate(cert); return ret; diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 0d164c6af539..fa73a6fd536c 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -15,6 +15,7 @@ #define _LINUX_PUBLIC_KEY_H #include +#include #include enum pkey_algo { @@ -98,8 +99,8 @@ struct key; extern int verify_signature(const struct key *key, const struct public_key_signature *sig); +struct asymmetric_key_id; extern struct key *x509_request_asymmetric_key(struct key *keyring, - const char *issuer, - const char *key_id); + const struct asymmetric_key_id *kid); #endif /* _LINUX_PUBLIC_KEY_H */ -- cgit v1.2.3 From 41559420003cfe99522257dded7793192c77b4e9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:15 +0100 Subject: PKCS#7: Better handling of unsupported crypto Provide better handling of unsupported crypto when verifying a PKCS#7 message. If we can't bridge the gap between a pair of X.509 certs or between a signed info block and an X.509 cert because it involves some crypto we don't support, that's not necessarily the end of the world as there may be other ways points at which we can intersect with a ring of trusted keys. Instead, only produce ENOPKG immediately if all the signed info blocks in a PKCS#7 message require unsupported crypto to bridge to the first X.509 cert. Otherwise, we defer the generation of ENOPKG until we get ENOKEY during trust validation. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/pkcs7_parser.h | 1 + crypto/asymmetric_keys/pkcs7_trust.c | 29 +++++++++++++------- crypto/asymmetric_keys/pkcs7_verify.c | 46 +++++++++++++++++++++++++++++--- crypto/asymmetric_keys/x509_parser.h | 1 + crypto/asymmetric_keys/x509_public_key.c | 13 +++++++-- 5 files changed, 74 insertions(+), 16 deletions(-) diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h index 91949f92bc72..efc7dc9b8f9c 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.h +++ b/crypto/asymmetric_keys/pkcs7_parser.h @@ -23,6 +23,7 @@ struct pkcs7_signed_info { struct x509_certificate *signer; /* Signing certificate (in msg->certs) */ unsigned index; bool trusted; + bool unsupported_crypto; /* T if not usable due to missing crypto */ /* Message digest - the digest of the Content Data (or NULL) */ const void *msgdigest; diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 09197e50fa82..8bd474e5e706 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -35,6 +35,11 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, kenter(",%u,", sinfo->index); + if (sinfo->unsupported_crypto) { + kleave(" = -ENOPKG [cached]"); + return -ENOPKG; + } + for (x509 = sinfo->signer; x509; x509 = x509->signer) { if (x509->seen) { if (x509->verified) { @@ -139,24 +144,28 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, { struct pkcs7_signed_info *sinfo; struct x509_certificate *p; - int cached_ret = 0, ret; + int cached_ret = -ENOKEY; + int ret; for (p = pkcs7->certs; p; p = p->next) p->seen = false; for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); - if (ret < 0) { - if (ret == -ENOPKG) { + switch (ret) { + case -ENOKEY: + continue; + case -ENOPKG: + if (cached_ret == -ENOKEY) cached_ret = -ENOPKG; - } else if (ret == -ENOKEY) { - if (cached_ret == 0) - cached_ret = -ENOKEY; - } else { - return ret; - } + continue; + case 0: + *_trusted |= sinfo->trusted; + cached_ret = 0; + continue; + default: + return ret; } - *_trusted |= sinfo->trusted; } return cached_ret; diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index 57e90fa17f2b..bd264052f751 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -181,7 +181,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, x509->seen = true; ret = x509_get_sig_params(x509); if (ret < 0) - return ret; + goto maybe_missing_crypto_in_x509; pr_debug("- issuer %s\n", x509->issuer); if (x509->authority) @@ -203,7 +203,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, ret = x509_check_signature(x509->pub, x509); if (ret < 0) - return ret; + goto maybe_missing_crypto_in_x509; x509->signer = x509; pr_debug("- self-signed\n"); return 0; @@ -245,6 +245,17 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, x509 = p; might_sleep(); } + +maybe_missing_crypto_in_x509: + /* Just prune the certificate chain at this point if we lack some + * crypto module to go further. Note, however, we don't want to set + * sinfo->missing_crypto as the signed info block may still be + * validatable against an X.509 cert lower in the chain that we have a + * trusted copy of. + */ + if (ret == -ENOPKG) + return 0; + return ret; } /* @@ -286,11 +297,33 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, /** * pkcs7_verify - Verify a PKCS#7 message * @pkcs7: The PKCS#7 message to be verified + * + * Verify a PKCS#7 message is internally consistent - that is, the data digest + * matches the digest in the AuthAttrs and any signature in the message or one + * of the X.509 certificates it carries that matches another X.509 cert in the + * message can be verified. + * + * This does not look to match the contents of the PKCS#7 message against any + * external public keys. + * + * Returns, in order of descending priority: + * + * (*) -EKEYREJECTED if a signature failed to match for which we found an + * appropriate X.509 certificate, or: + * + * (*) -EBADMSG if some part of the message was invalid, or: + * + * (*) -ENOPKG if none of the signature chains are verifiable because suitable + * crypto modules couldn't be found, or: + * + * (*) 0 if all the signature chains that don't incur -ENOPKG can be verified + * (note that a signature chain may be of zero length), or: */ int pkcs7_verify(struct pkcs7_message *pkcs7) { struct pkcs7_signed_info *sinfo; struct x509_certificate *x509; + int enopkg = -ENOPKG; int ret, n; kenter(""); @@ -306,12 +339,17 @@ int pkcs7_verify(struct pkcs7_message *pkcs7) for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { ret = pkcs7_verify_one(pkcs7, sinfo); if (ret < 0) { + if (ret == -ENOPKG) { + sinfo->unsupported_crypto = true; + continue; + } kleave(" = %d", ret); return ret; } + enopkg = 0; } - kleave(" = 0"); - return 0; + kleave(" = %d", enopkg); + return enopkg; } EXPORT_SYMBOL_GPL(pkcs7_verify); diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index 0e8d59b010fb..4e1a384901ed 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -38,6 +38,7 @@ struct x509_certificate { bool seen; /* Infinite recursion prevention */ bool verified; bool trusted; + bool unsupported_crypto; /* T if can't be verified due to missing crypto */ }; /* diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index c60905c3f4d2..1d9a4c555376 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -115,6 +115,8 @@ int x509_get_sig_params(struct x509_certificate *cert) pr_devel("==>%s()\n", __func__); + if (cert->unsupported_crypto) + return -ENOPKG; if (cert->sig.rsa.s) return 0; @@ -127,8 +129,13 @@ int x509_get_sig_params(struct x509_certificate *cert) * big the hash operational data will be. */ tfm = crypto_alloc_shash(hash_algo_name[cert->sig.pkey_hash_algo], 0, 0); - if (IS_ERR(tfm)) - return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); + if (IS_ERR(tfm)) { + if (PTR_ERR(tfm) == -ENOENT) { + cert->unsupported_crypto = true; + return -ENOPKG; + } + return PTR_ERR(tfm); + } desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); digest_size = crypto_shash_digestsize(tfm); @@ -175,6 +182,8 @@ int x509_check_signature(const struct public_key *pub, return ret; ret = public_key_verify_signature(pub, &cert->sig); + if (ret == -ENOPKG) + cert->unsupported_crypto = true; pr_debug("Cert Verification: %d\n", ret); return ret; } -- cgit v1.2.3 From 757932e6da6de05b91cc4cb85493b17de48c2667 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:17 +0100 Subject: PKCS#7: Handle PKCS#7 messages that contain no X.509 certs The X.509 certificate list in a PKCS#7 message is optional. To save space, we can omit the inclusion of any X.509 certificates if we are sure that we can look the relevant public key up by the serial number and issuer given in a signed info block. This also supports use of a signed info block for which we can't find a matching X.509 cert in the certificate list, though it be populated. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/pkcs7_trust.c | 48 ++++++++++++++++++++++++++--------- crypto/asymmetric_keys/pkcs7_verify.c | 16 ++++++++---- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 8bd474e5e706..ae47be6128c4 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -55,13 +55,16 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * keys. */ key = x509_request_asymmetric_key(trust_keyring, x509->id); - if (!IS_ERR(key)) + if (!IS_ERR(key)) { /* One of the X.509 certificates in the PKCS#7 message * is apparently the same as one we already trust. * Verify that the trusted variant can also validate * the signature on the descendant. */ + pr_devel("sinfo %u: Cert %u as key %x\n", + sinfo->index, x509->index, key_serial(key)); goto matched; + } if (key == ERR_PTR(-ENOMEM)) return -ENOMEM; @@ -81,15 +84,34 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, /* No match - see if the root certificate has a signer amongst the * trusted keys. */ - if (!last || !last->issuer || !last->authority) { - kleave(" = -ENOKEY [no backref]"); - return -ENOKEY; + if (last && last->authority) { + key = x509_request_asymmetric_key(trust_keyring, last->authority); + if (!IS_ERR(key)) { + x509 = last; + pr_devel("sinfo %u: Root cert %u signer is key %x\n", + sinfo->index, x509->index, key_serial(key)); + goto matched; + } + if (PTR_ERR(key) != -ENOKEY) + return PTR_ERR(key); } - key = x509_request_asymmetric_key(trust_keyring, last->authority); - if (IS_ERR(key)) - return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY; - x509 = last; + /* As a last resort, see if we have a trusted public key that matches + * the signed info directly. + */ + key = x509_request_asymmetric_key(trust_keyring, + sinfo->signing_cert_id); + if (!IS_ERR(key)) { + pr_devel("sinfo %u: Direct signer is key %x\n", + sinfo->index, key_serial(key)); + x509 = NULL; + goto matched; + } + if (PTR_ERR(key) != -ENOKEY) + return PTR_ERR(key); + + kleave(" = -ENOKEY [no backref]"); + return -ENOKEY; matched: ret = verify_signature(key, sig); @@ -103,10 +125,12 @@ matched: } verified: - x509->verified = true; - for (p = sinfo->signer; p != x509; p = p->signer) { - p->verified = true; - p->trusted = trusted; + if (x509) { + x509->verified = true; + for (p = sinfo->signer; p != x509; p = p->signer) { + p->verified = true; + p->trusted = trusted; + } } sinfo->trusted = trusted; kleave(" = 0"); diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index bd264052f751..cd455450b069 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -154,10 +154,13 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, return 0; } - pr_warn("Sig %u: Issuing X.509 cert not found (#%*ph)\n", - sinfo->index, - sinfo->signing_cert_id->len, sinfo->signing_cert_id->data); - return -ENOKEY; + /* The relevant X.509 cert isn't found here, but it might be found in + * the trust keyring. + */ + pr_debug("Sig %u: Issuing X.509 cert not found (#%*phN)\n", + sinfo->index, + sinfo->signing_cert_id->len, sinfo->signing_cert_id->data); + return 0; } /* @@ -275,11 +278,14 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, if (ret < 0) return ret; - /* Find the key for the signature */ + /* Find the key for the signature if there is one */ ret = pkcs7_find_key(pkcs7, sinfo); if (ret < 0) return ret; + if (!sinfo->signer) + return 0; + pr_devel("Using X.509[%u] for sig %u\n", sinfo->signer->index, sinfo->index); -- cgit v1.2.3 From 31b70f66328e85517b159c786ab31f3fd9a7293c Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 27 Jun 2014 13:01:32 +0300 Subject: ima: move keyring initialization to ima_init() ima_init() is used as a single place for all initializations. Experimental keyring patches used the 'late_initcall' which was co-located with the late_initcall(init_ima). When the late_initcall for the keyring initialization was abandoned, initialization moved to init_ima, though it would be more logical to move it to ima_init, where the rest of the initialization is done. This patch moves the keyring initialization to ima_init() as a preparatory step for loading the keys which will be added to ima_init() in following patches. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_init.c | 4 ++++ security/integrity/ima/ima_main.c | 10 ++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index e8f9d70a465d..8cf0f39c8cd2 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -98,6 +98,10 @@ int __init ima_init(void) if (!ima_used_chip) pr_info("No TPM chip found, activating TPM-bypass!\n"); + rc = ima_init_keyring(INTEGRITY_KEYRING_IMA); + if (rc) + return rc; + rc = ima_init_crypto(); if (rc) return rc; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 673a37e92ba3..ed7d9fa4f536 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -334,14 +334,8 @@ static int __init init_ima(void) hash_setup(CONFIG_IMA_DEFAULT_HASH); error = ima_init(); - if (error) - goto out; - - error = ima_init_keyring(INTEGRITY_KEYRING_IMA); - if (error) - goto out; - ima_initialized = 1; -out: + if (!error) + ima_initialized = 1; return error; } -- cgit v1.2.3 From 2faa6ef3b21152cc05b69a84113625dcee63176f Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 8 May 2014 13:11:29 +0300 Subject: ima: provide 'ima_appraise=log' kernel option The kernel boot parameter "ima_appraise" currently defines 'off', 'enforce' and 'fix' modes. When designing a policy and labeling the system, access to files are either blocked in the default 'enforce' mode or automatically fixed in the 'fix' mode. It is beneficial to be able to run the system in a logging only mode, without fixing it, in order to properly analyze the system. This patch adds a 'log' mode to run the system in a permissive mode and log the appraisal results. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- Documentation/kernel-parameters.txt | 2 +- security/integrity/ima/ima.h | 5 +++-- security/integrity/ima/ima_appraise.c | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 90c12c591168..2aa1b6e74aca 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1292,7 +1292,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Set number of hash buckets for inode cache. ima_appraise= [IMA] appraise integrity measurements - Format: { "off" | "enforce" | "fix" } + Format: { "off" | "enforce" | "fix" | "log" } default: "enforce" ima_appraise_tcb [IMA] diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 8e4bb883fc13..d61680dcd365 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -159,8 +159,9 @@ void ima_delete_rules(void); /* Appraise integrity measurements */ #define IMA_APPRAISE_ENFORCE 0x01 #define IMA_APPRAISE_FIX 0x02 -#define IMA_APPRAISE_MODULES 0x04 -#define IMA_APPRAISE_FIRMWARE 0x08 +#define IMA_APPRAISE_LOG 0x04 +#define IMA_APPRAISE_MODULES 0x08 +#define IMA_APPRAISE_FIRMWARE 0x10 #ifdef CONFIG_IMA_APPRAISE int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 013ec3f0e42d..2dc13fbb7e91 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -23,6 +23,8 @@ static int __init default_appraise_setup(char *str) { if (strncmp(str, "off", 3) == 0) ima_appraise = 0; + else if (strncmp(str, "log", 3) == 0) + ima_appraise = IMA_APPRAISE_LOG; else if (strncmp(str, "fix", 3) == 0) ima_appraise = IMA_APPRAISE_FIX; return 1; -- cgit v1.2.3 From be39ffc2fec78ff80d50e4b7970e94a8b1583862 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 12 Sep 2014 19:35:53 +0200 Subject: ima: return an error code from ima_add_boot_aggregate() This patch modifies ima_add_boot_aggregate() to return an error code. This way we can determine if all the initialization procedures have been executed successfully. Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_init.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 8cf0f39c8cd2..9164fc8cac84 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -43,7 +43,7 @@ int ima_used_chip; * a different value.) Violations add a zero entry to the measurement * list and extend the aggregate PCR value with ff...ff's. */ -static void __init ima_add_boot_aggregate(void) +static int __init ima_add_boot_aggregate(void) { static const char op[] = "add_boot_aggregate"; const char *audit_cause = "ENOMEM"; @@ -72,17 +72,23 @@ static void __init ima_add_boot_aggregate(void) result = ima_alloc_init_template(iint, NULL, boot_aggregate_name, NULL, 0, &entry); - if (result < 0) - return; + if (result < 0) { + audit_cause = "alloc_entry"; + goto err_out; + } result = ima_store_template(entry, violation, NULL, boot_aggregate_name); - if (result < 0) + if (result < 0) { ima_free_template_entry(entry); - return; + audit_cause = "store_entry"; + goto err_out; + } + return 0; err_out: integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op, audit_cause, result, 0); + return result; } int __init ima_init(void) @@ -109,7 +115,10 @@ int __init ima_init(void) if (rc != 0) return rc; - ima_add_boot_aggregate(); /* boot aggregate must be first entry */ + rc = ima_add_boot_aggregate(); /* boot aggregate must be first entry */ + if (rc != 0) + return rc; + ima_init_policy(); return ima_fs_init(); -- cgit v1.2.3 From a756024efea259282e65f3a00f512b094e805d76 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 12 Sep 2014 19:35:54 +0200 Subject: ima: added ima_policy_flag variable This patch introduces the new variable 'ima_policy_flag', whose bits are set depending on the action of the current policy rules. Only the flags IMA_MEASURE, IMA_APPRAISE and IMA_AUDIT are set. The new variable will be used to improve performance by skipping the unnecessary execution of IMA code if the policy does not contain rules with the above actions. Changes in v6 (Roberto Sassu) * do not check 'ima_initialized' before calling ima_update_policy_flag() in ima_update_policy() (suggested by Dmitry) * calling ima_update_policy_flag() moved to init_ima to co-locate with ima_initialized (Dmitry) * add/revise comments (Mimi) Changes in v5 (Roberto Sassu) * reset IMA_APPRAISE flag in 'ima_policy_flag' if 'ima_appraise' is set to zero (reported by Dmitry) * update 'ima_policy_flag' only if IMA initialization is successful (suggested by Mimi and Dmitry) * check 'ima_policy_flag' instead of 'ima_initialized' (suggested by Mimi and Dmitry) Signed-off-by: Roberto Sassu Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 4 ++++ security/integrity/ima/ima_appraise.c | 4 ++-- security/integrity/ima/ima_main.c | 8 +++++--- security/integrity/ima/ima_policy.c | 23 +++++++++++++++++++++++ 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index d61680dcd365..8ee997dff139 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -43,6 +43,9 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; #define IMA_TEMPLATE_IMA_NAME "ima" #define IMA_TEMPLATE_IMA_FMT "d|n" +/* current content of the policy */ +extern int ima_policy_flag; + /* set during initialization */ extern int ima_initialized; extern int ima_used_chip; @@ -153,6 +156,7 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, int flags); void ima_init_policy(void); void ima_update_policy(void); +void ima_update_policy_flag(void); ssize_t ima_parse_add_rule(char *); void ima_delete_rules(void); diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 2dc13fbb7e91..922685483bd3 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -318,7 +318,7 @@ void ima_inode_post_setattr(struct dentry *dentry) struct integrity_iint_cache *iint; int must_appraise, rc; - if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode) + if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode) || !inode->i_op->removexattr) return; @@ -356,7 +356,7 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig) { struct integrity_iint_cache *iint; - if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)) + if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)) return; iint = integrity_iint_find(inode); diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index ed7d9fa4f536..2191b36ad1da 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -85,7 +85,7 @@ static void ima_rdwr_violation_check(struct file *file) char *pathbuf = NULL; const char *pathname; - if (!S_ISREG(inode->i_mode) || !ima_initialized) + if (!S_ISREG(inode->i_mode) || !(ima_policy_flag & IMA_MEASURE)) return; if (mode & FMODE_WRITE) { @@ -168,7 +168,7 @@ static int process_measurement(struct file *file, int mask, int function, struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL; int xattr_len = 0; - if (!ima_initialized || !S_ISREG(inode->i_mode)) + if (!ima_policy_flag || !S_ISREG(inode->i_mode)) return 0; /* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action @@ -334,8 +334,10 @@ static int __init init_ima(void) hash_setup(CONFIG_IMA_DEFAULT_HASH); error = ima_init(); - if (!error) + if (!error) { ima_initialized = 1; + ima_update_policy_flag(); + } return error; } diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 07099a8bc283..cdc620b2152f 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -35,6 +35,8 @@ #define DONT_APPRAISE 0x0008 #define AUDIT 0x0040 +int ima_policy_flag; + #define MAX_LSM_RULES 6 enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE @@ -295,6 +297,26 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, return action; } +/* + * Initialize the ima_policy_flag variable based on the currently + * loaded policy. Based on this flag, the decision to short circuit + * out of a function or not call the function in the first place + * can be made earlier. + */ +void ima_update_policy_flag(void) +{ + struct ima_rule_entry *entry; + + ima_policy_flag = 0; + list_for_each_entry(entry, ima_rules, list) { + if (entry->action & IMA_DO_MASK) + ima_policy_flag |= entry->action; + } + + if (!ima_appraise) + ima_policy_flag &= ~IMA_APPRAISE; +} + /** * ima_init_policy - initialize the default measure rules. * @@ -341,6 +363,7 @@ void ima_update_policy(void) if (ima_rules == &ima_default_rules) { ima_rules = &ima_policy_rules; + ima_update_policy_flag(); cause = "complete"; result = 0; } -- cgit v1.2.3 From f7a859ff7395c0ffe60f9563df5354473e5f9244 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 12 Sep 2014 19:35:55 +0200 Subject: ima: fix race condition on ima_rdwr_violation_check and process_measurement This patch fixes a race condition between two functions that try to access the same inode. Since the i_mutex lock is held and released separately in the two functions, there may be the possibility that a violation is not correctly detected. Suppose there are two processes, A (reader) and B (writer), if the following sequence happens: A: ima_rdwr_violation_check() B: ima_rdwr_violation_check() B: process_measurement() B: starts writing the inode A: process_measurement() the ToMToU violation (a reader may be accessing a content different from that measured, due to a concurrent modification by a writer) will not be detected. To avoid this issue, the violation check and the measurement must be done atomically. This patch fixes the problem by moving the violation check inside process_measurement() when the i_mutex lock is held. Differently from the old code, the violation check is executed also for the MMAP_CHECK hook (other than for FILE_CHECK). This allows to detect ToMToU violations that are possible because shared libraries can be opened for writing while they are in use (according to the output of 'man mmap', the mmap() flag MAP_DENYWRITE is ignored). Changes in v5 (Roberto Sassu): * get iint if action is not zero * exit process_measurement() after the violation check if action is zero * reverse order process_measurement() exit cleanup (Mimi) Changes in v4 (Dmitry Kasatkin): * iint allocation is done before calling ima_rdrw_violation_check() (Suggested-by Mimi) * do not check for violations if the policy does not contain 'measure' rules (done by Roberto Sassu) Changes in v3 (Dmitry Kasatkin): * no violation checking for MMAP_CHECK function in this patch * remove use of filename from violation * removes checking if ima is enabled from ima_rdrw_violation_check * slight style change Suggested-by: Dmitry Kasatkin Signed-off-by: Roberto Sassu Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_main.c | 54 ++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 2191b36ad1da..03bb52ecf490 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -77,21 +77,19 @@ __setup("ima_hash=", hash_setup); * could result in a file measurement error. * */ -static void ima_rdwr_violation_check(struct file *file) +static void ima_rdwr_violation_check(struct file *file, + struct integrity_iint_cache *iint, + char **pathbuf, + const char **pathname) { struct inode *inode = file_inode(file); fmode_t mode = file->f_mode; bool send_tomtou = false, send_writers = false; - char *pathbuf = NULL; - const char *pathname; - - if (!S_ISREG(inode->i_mode) || !(ima_policy_flag & IMA_MEASURE)) - return; if (mode & FMODE_WRITE) { if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) { - struct integrity_iint_cache *iint; - iint = integrity_iint_find(inode); + if (!iint) + iint = integrity_iint_find(inode); /* IMA_MEASURE is set from reader side */ if (iint && (iint->flags & IMA_MEASURE)) send_tomtou = true; @@ -105,14 +103,13 @@ static void ima_rdwr_violation_check(struct file *file) if (!send_tomtou && !send_writers) return; - pathname = ima_d_path(&file->f_path, &pathbuf); + *pathname = ima_d_path(&file->f_path, pathbuf); if (send_tomtou) - ima_add_violation(file, pathname, "invalid_pcr", "ToMToU"); + ima_add_violation(file, *pathname, "invalid_pcr", "ToMToU"); if (send_writers) - ima_add_violation(file, pathname, + ima_add_violation(file, *pathname, "invalid_pcr", "open_writers"); - kfree(pathbuf); } static void ima_check_last_writer(struct integrity_iint_cache *iint, @@ -160,13 +157,14 @@ static int process_measurement(struct file *file, int mask, int function, int opened) { struct inode *inode = file_inode(file); - struct integrity_iint_cache *iint; + struct integrity_iint_cache *iint = NULL; struct ima_template_desc *template_desc; char *pathbuf = NULL; const char *pathname = NULL; int rc = -ENOMEM, action, must_appraise; struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL; int xattr_len = 0; + bool violation_check; if (!ima_policy_flag || !S_ISREG(inode->i_mode)) return 0; @@ -176,7 +174,9 @@ static int process_measurement(struct file *file, int mask, int function, * Included is the appraise submask. */ action = ima_get_action(inode, mask, function); - if (!action) + violation_check = (function == FILE_CHECK && + (ima_policy_flag & IMA_MEASURE)); + if (!action && !violation_check) return 0; must_appraise = action & IMA_APPRAISE; @@ -187,9 +187,19 @@ static int process_measurement(struct file *file, int mask, int function, mutex_lock(&inode->i_mutex); - iint = integrity_inode_get(inode); - if (!iint) - goto out; + if (action) { + iint = integrity_inode_get(inode); + if (!iint) + goto out; + } + + if (violation_check) { + ima_rdwr_violation_check(file, iint, &pathbuf, &pathname); + if (!action) { + rc = 0; + goto out_free; + } + } /* Determine if already appraised/measured based on bitmask * (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED, @@ -218,7 +228,8 @@ static int process_measurement(struct file *file, int mask, int function, goto out_digsig; } - pathname = ima_d_path(&file->f_path, &pathbuf); + if (!pathname) /* ima_rdwr_violation possibly pre-fetched */ + pathname = ima_d_path(&file->f_path, &pathbuf); if (action & IMA_MEASURE) ima_store_measurement(iint, file, pathname, @@ -228,13 +239,15 @@ static int process_measurement(struct file *file, int mask, int function, xattr_value, xattr_len, opened); if (action & IMA_AUDIT) ima_audit_measurement(iint, pathname); - kfree(pathbuf); + out_digsig: if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG)) rc = -EACCES; + kfree(xattr_value); +out_free: + kfree(pathbuf); out: mutex_unlock(&inode->i_mutex); - kfree(xattr_value); if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE)) return -EACCES; return 0; @@ -288,7 +301,6 @@ int ima_bprm_check(struct linux_binprm *bprm) */ int ima_file_check(struct file *file, int mask, int opened) { - ima_rdwr_violation_check(file); return process_measurement(file, mask & (MAY_READ | MAY_WRITE | MAY_EXEC), FILE_CHECK, opened); -- cgit v1.2.3 From 1b68bdf9cded82d37e443a20c5ed47bbb084d5dc Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 12 Sep 2014 19:35:56 +0200 Subject: ima: detect violations for mmaped files This patch fixes the detection of the 'open_writers' violation for mmaped files. before) an 'open_writers' violation is detected if the policy contains a rule with the criteria: func=FILE_CHECK mask=MAY_READ after) an 'open_writers' violation is detected if the current event matches one of the policy rules. With the old behaviour, the 'open_writers' violation is not detected in the following case: policy: measure func=FILE_MMAP mask=MAY_EXEC steps: 1) open a shared library for writing 2) execute a binary that links that shared library 3) during the binary execution, modify the shared library and save the change result: the 'open_writers' violation measurement is not present in the IMA list. Only binaries executed are protected from writes. For libraries mapped in memory there is the flag MAP_DENYWRITE for this purpose, but according to the output of 'man mmap', the mmap flag is ignored. Since ima_rdwr_violation_check() is now called by process_measurement() the information about if the inode must be measured is already provided by ima_get_action(). Thus the unnecessary function ima_must_measure() has been removed. Changes in v3 (Dmitry Kasatkin): - Violation for MMAP_CHECK function are verified since this patch - Changed patch description a bit Signed-off-by: Roberto Sassu Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_api.c | 5 ----- security/integrity/ima/ima_main.c | 9 +++++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 65c41a968cc1..86885979918c 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -179,11 +179,6 @@ int ima_get_action(struct inode *inode, int mask, int function) return ima_match_policy(inode, function, mask, flags); } -int ima_must_measure(struct inode *inode, int mask, int function) -{ - return ima_match_policy(inode, function, mask, IMA_MEASURE); -} - /* * ima_collect_measurement - collect file measurement * diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 03bb52ecf490..62f59eca32d3 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -79,6 +79,7 @@ __setup("ima_hash=", hash_setup); */ static void ima_rdwr_violation_check(struct file *file, struct integrity_iint_cache *iint, + int must_measure, char **pathbuf, const char **pathname) { @@ -95,8 +96,7 @@ static void ima_rdwr_violation_check(struct file *file, send_tomtou = true; } } else { - if ((atomic_read(&inode->i_writecount) > 0) && - ima_must_measure(inode, MAY_READ, FILE_CHECK)) + if ((atomic_read(&inode->i_writecount) > 0) && must_measure) send_writers = true; } @@ -174,7 +174,7 @@ static int process_measurement(struct file *file, int mask, int function, * Included is the appraise submask. */ action = ima_get_action(inode, mask, function); - violation_check = (function == FILE_CHECK && + violation_check = ((function == FILE_CHECK || function == MMAP_CHECK) && (ima_policy_flag & IMA_MEASURE)); if (!action && !violation_check) return 0; @@ -194,7 +194,8 @@ static int process_measurement(struct file *file, int mask, int function, } if (violation_check) { - ima_rdwr_violation_check(file, iint, &pathbuf, &pathname); + ima_rdwr_violation_check(file, iint, action & IMA_MEASURE, + &pathbuf, &pathname); if (!action) { rc = 0; goto out_free; -- cgit v1.2.3 From d1ac5540455c3a2a11e943e19e2dc044cebe147d Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Sep 2014 00:02:01 +0100 Subject: KEYS: Check hex2bin()'s return when generating an asymmetric key ID As it stands, the code to generate an asymmetric key ID prechecks the hex string it is given whilst determining the length, before it allocates the buffer for hex2bin() to translate into - which mean that checking the result of hex2bin() is redundant. Unfortunately, hex2bin() is marked as __must_check, which means that the following warning may be generated if the return value isn't checked: crypto/asymmetric_keys/asymmetric_type.c: In function asymmetric_key_hex_to_key_id: crypto/asymmetric_keys/asymmetric_type.c:110: warning: ignoring return value of hex2bin, declared with attribute warn_unused_result The warning can't be avoided by casting the result to void. Instead, use strlen() to check the length of the string and ignore the fact that the string might not be entirely valid hex until after the allocation has been done - in which case we can use the result of hex2bin() for this. Signed-off-by: David Howells --- crypto/asymmetric_keys/asymmetric_type.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 718e779a010e..f0f2111d2c66 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -90,15 +90,12 @@ EXPORT_SYMBOL_GPL(asymmetric_match_key_ids); struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id) { struct asymmetric_key_id *match_id; - const char *p; - ptrdiff_t hexlen; + size_t hexlen; + int ret; if (!*id) return ERR_PTR(-EINVAL); - for (p = id; *p; p++) - if (!isxdigit(*p)) - return ERR_PTR(-EINVAL); - hexlen = p - id; + hexlen = strlen(id); if (hexlen & 1) return ERR_PTR(-EINVAL); @@ -107,7 +104,11 @@ struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id) if (!match_id) return ERR_PTR(-ENOMEM); match_id->len = hexlen / 2; - (void)hex2bin(match_id->data, id, hexlen / 2); + ret = hex2bin(match_id->data, id, hexlen / 2); + if (ret < 0) { + kfree(match_id); + return ERR_PTR(-EINVAL); + } return match_id; } -- cgit v1.2.3 From e173fb2646a832b424c80904c306b816760ce477 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Thu, 18 Sep 2014 20:50:17 -0400 Subject: selinux: cleanup error reporting in selinux_nlmsg_perm() Convert audit_log() call to WARN_ONCE(). Rename "type=" to nlmsg_type=" to avoid confusion with the audit record type. Added "protocol=" to help track down which protocol (NETLINK_AUDIT?) was used within the netlink protocol family. Signed-off-by: Richard Guy Briggs [Rewrote the patch subject line] Signed-off-by: Paul Moore --- security/selinux/hooks.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5eb512b7ac31..29e64d4ca099 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4727,10 +4727,9 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); if (err) { if (err == -EINVAL) { - audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR, - "SELinux: unrecognized netlink message" - " type=%hu for sclass=%hu\n", - nlh->nlmsg_type, sksec->sclass); + WARN_ONCE(1, "selinux_nlmsg_perm: unrecognized netlink message:" + " protocol=%hu nlmsg_type=%hu sclass=%hu\n", + sk->sk_protocol, nlh->nlmsg_type, sksec->sclass); if (!selinux_enforcing || security_get_allow_unknown()) err = 0; } -- cgit v1.2.3 From 4093a8443941d7021c7f747474a87a56cf666270 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Thu, 18 Sep 2014 20:47:48 -0400 Subject: selinux: normalize audit log formatting Restructure to keyword=value pairs without spaces. Drop superfluous words in text. Make invalid_context a keyword. Change result= keyword to seresult=. Signed-off-by: Richard Guy Briggs [Minor rewrite to the patch subject line] Signed-off-by: Paul Moore --- security/selinux/ss/services.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 2aa9d172dc7e..a1d3944751b9 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -728,7 +728,7 @@ static int security_validtrans_handle_fail(struct context *ocontext, if (context_struct_to_string(tcontext, &t, &tlen)) goto out; audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, - "security_validate_transition: denied for" + "op=security_validate_transition seresult=denied" " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s", o, n, t, sym_name(&policydb, SYM_CLASSES, tclass-1)); out: @@ -877,7 +877,7 @@ int security_bounded_transition(u32 old_sid, u32 new_sid) audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, "op=security_bounded_transition " - "result=denied " + "seresult=denied " "oldcontext=%s newcontext=%s", old_name, new_name); } @@ -1351,8 +1351,8 @@ static int compute_sid_handle_invalid_context( if (context_struct_to_string(newcontext, &n, &nlen)) goto out; audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, - "security_compute_sid: invalid context %s" - " for scontext=%s" + "op=security_compute_sid invalid_context=%s" + " scontext=%s" " tcontext=%s" " tclass=%s", n, s, t, sym_name(&policydb, SYM_CLASSES, tclass-1)); @@ -2607,8 +2607,10 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid) rc = convert_context_handle_invalid_context(&newcon); if (rc) { if (!context_struct_to_string(&newcon, &s, &len)) { - audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, - "security_sid_mls_copy: invalid context %s", s); + audit_log(current->audit_context, + GFP_ATOMIC, AUDIT_SELINUX_ERR, + "op=security_sid_mls_copy " + "invalid_context=%s", s); kfree(s); } goto out_unlock; -- cgit v1.2.3 From 40b50e80c5ca78b3164d79d39b4889c4e58f462e Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 3 Oct 2014 11:53:28 +0300 Subject: KEYS: handle error code encoded in pointer If hexlen is odd then function returns an error. Use IS_ERR to check for error, otherwise invalid pointer is used and kernel gives oops: [ 132.816522] BUG: unable to handle kernel paging request at ffffffffffffffea [ 132.819902] IP: [] asymmetric_key_id_same+0x14/0x36 [ 132.820302] PGD 1a12067 PUD 1a14067 PMD 0 [ 132.820302] Oops: 0000 [#1] SMP [ 132.820302] Modules linked in: bridge(E) stp(E) llc(E) evdev(E) serio_raw(E) i2c_piix4(E) button(E) fuse(E) [ 132.820302] CPU: 0 PID: 2993 Comm: cat Tainted: G E 3.16.0-kds+ #2847 [ 132.820302] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 [ 132.820302] task: ffff88004249a430 ti: ffff880056640000 task.ti: ffff880056640000 [ 132.820302] RIP: 0010:[] [] asymmetric_key_id_same+0x14/0x36 [ 132.820302] RSP: 0018:ffff880056643930 EFLAGS: 00010246 [ 132.820302] RAX: 0000000000000000 RBX: ffffffffffffffea RCX: ffff880056643ae0 [ 132.820302] RDX: 000000000000005e RSI: ffffffffffffffea RDI: ffff88005bac9300 [ 132.820302] RBP: ffff880056643948 R08: 0000000000000003 R09: 00000007504aa01a [ 132.820302] R10: 0000000000000000 R11: 0000000000000000 R12: ffff88005d68ca40 [ 132.820302] R13: 0000000000000101 R14: 0000000000000000 R15: ffff88005bac5280 [ 132.820302] FS: 00007f67a153c740(0000) GS:ffff88005da00000(0000) knlGS:0000000000000000 [ 132.820302] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 132.820302] CR2: ffffffffffffffea CR3: 000000002e663000 CR4: 00000000000006f0 [ 132.820302] Stack: [ 132.820302] ffffffff812bfc66 ffff880056643ae0 ffff88005bac5280 ffff880056643958 [ 132.820302] ffffffff812bfc9d ffff880056643980 ffffffff812971d9 ffff88005ce930c1 [ 132.820302] ffff88005ce930c0 0000000000000000 ffff8800566439c8 ffffffff812fb753 [ 132.820302] Call Trace: [ 132.820302] [] ? asymmetric_match_key_ids+0x24/0x42 [ 132.820302] [] asymmetric_key_cmp+0x19/0x1b [ 132.820302] [] keyring_search_iterator+0x74/0xd7 [ 132.820302] [] assoc_array_subtree_iterate+0x67/0xd2 [ 132.820302] [] ? key_default_cmp+0x20/0x20 [ 132.820302] [] assoc_array_iterate+0x19/0x1e [ 132.820302] [] search_nested_keyrings+0xf6/0x2b6 [ 132.820302] [] ? sched_clock_cpu+0x91/0xa2 [ 132.820302] [] ? mark_held_locks+0x58/0x6e [ 132.820302] [] ? current_kernel_time+0x77/0xb8 [ 132.820302] [] keyring_search_aux+0xe1/0x14c [ 132.820302] [] ? keyring_search_aux+0x6c/0x14c [ 132.820302] [] keyring_search+0x8f/0xb6 [ 132.820302] [] ? asymmetric_match_key_ids+0x42/0x42 [ 132.820302] [] ? key_default_cmp+0x20/0x20 [ 132.820302] [] asymmetric_verify+0xa4/0x214 [ 132.820302] [] integrity_digsig_verify+0xb1/0xe2 [ 132.820302] [] ? evm_verifyxattr+0x6a/0x7a [ 132.820302] [] ima_appraise_measurement+0x160/0x370 [ 132.820302] [] ? d_absolute_path+0x5b/0x7a [ 132.820302] [] process_measurement+0x322/0x404 Reported-by: Dmitry Kasatkin Signed-off-by: Dmitry Kasatkin Signed-off-by: David Howells --- crypto/asymmetric_keys/asymmetric_type.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index f0f2111d2c66..29983cbb658d 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -153,8 +153,8 @@ static int asymmetric_key_match_preparse(struct key_match_data *match_data) } match_id = asymmetric_key_hex_to_key_id(id); - if (!match_id) - return -ENOMEM; + if (IS_ERR(match_id)) + return PTR_ERR(match_id); match_data->preparsed = match_id; match_data->cmp = asymmetric_key_cmp; -- cgit v1.2.3 From dd2f6c4481debfa389c1f2b2b1d5bd6449c42611 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 3 Oct 2014 16:17:02 +0100 Subject: X.509: If available, use the raw subjKeyId to form the key description Module signing matches keys by comparing against the key description exactly. However, the way the key description gets constructed got changed to be composed of the subject name plus the certificate serial number instead of the subject name and the subjectKeyId. I changed this to avoid problems with certificates that don't *have* a subjectKeyId. Instead, if available, use the raw subjectKeyId to form the key description and only use the serial number if the subjectKeyId doesn't exist. Reported-by: Dmitry Kasatkin Signed-off-by: David Howells --- crypto/asymmetric_keys/x509_cert_parser.c | 2 ++ crypto/asymmetric_keys/x509_parser.h | 2 ++ crypto/asymmetric_keys/x509_public_key.c | 9 +++++++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 96151b2b91a2..393706f33fa5 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -435,6 +435,8 @@ int x509_process_extension(void *context, size_t hdrlen, v += 2; vlen -= 2; + ctx->cert->raw_skid_size = vlen; + ctx->cert->raw_skid = v; kid = asymmetric_key_generate_id(v, vlen, ctx->cert->raw_subject, ctx->cert->raw_subject_size); diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index 4e1a384901ed..3f0f0f081621 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -34,6 +34,8 @@ struct x509_certificate { const void *raw_issuer; /* Raw issuer name in ASN.1 */ const void *raw_subject; /* Raw subject name in ASN.1 */ unsigned raw_subject_size; + unsigned raw_skid_size; + const void *raw_skid; /* Raw subjectKeyId in ASN.1 */ unsigned index; bool seen; /* Infinite recursion prevention */ bool verified; diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 1d9a4c555376..8bffb06b2683 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -279,8 +279,13 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) /* Propose a description */ sulen = strlen(cert->subject); - srlen = cert->raw_serial_size; - q = cert->raw_serial; + if (cert->raw_skid) { + srlen = cert->raw_skid_size; + q = cert->raw_skid; + } else { + srlen = cert->raw_serial_size; + q = cert->raw_serial; + } if (srlen > 1 && *q == 0) { srlen--; q++; -- cgit v1.2.3 From f1b731dbc2530cab93fcfc5fcb18c9f3a100feeb Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 6 Oct 2014 15:21:05 +0100 Subject: KEYS: Restore partial ID matching functionality for asymmetric keys Bring back the functionality whereby an asymmetric key can be matched with a partial match on one of its IDs. Whilst we're at it, allow for the possibility of having an increased number of IDs. Reported-by: Dmitry Kasatkin Signed-off-by: Dmitry Kasatkin Signed-off-by: David Howells --- crypto/asymmetric_keys/asymmetric_keys.h | 3 -- crypto/asymmetric_keys/asymmetric_type.c | 70 ++++++++++++++++++++++++++------ crypto/asymmetric_keys/pkcs7_trust.c | 9 ++-- crypto/asymmetric_keys/x509_public_key.c | 18 +++++--- include/crypto/public_key.h | 3 +- include/keys/asymmetric-type.h | 3 ++ 6 files changed, 81 insertions(+), 25 deletions(-) diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h index fd21ac28e0a0..f97330886d58 100644 --- a/crypto/asymmetric_keys/asymmetric_keys.h +++ b/crypto/asymmetric_keys/asymmetric_keys.h @@ -9,9 +9,6 @@ * 2 of the Licence, or (at your option) any later version. */ -extern bool asymmetric_match_key_ids(const struct asymmetric_key_ids *kids, - const struct asymmetric_key_id *match_id); - extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id); static inline diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 29983cbb658d..052e944bb109 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -65,23 +65,44 @@ bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1, } EXPORT_SYMBOL_GPL(asymmetric_key_id_same); +/** + * asymmetric_key_id_partial - Return true if two asymmetric keys IDs + * partially match + * @kid_1, @kid_2: The key IDs to compare + */ +bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1, + const struct asymmetric_key_id *kid2) +{ + if (!kid1 || !kid2) + return false; + if (kid1->len < kid2->len) + return false; + return memcmp(kid1->data + (kid1->len - kid2->len), + kid2->data, kid2->len) == 0; +} +EXPORT_SYMBOL_GPL(asymmetric_key_id_partial); + /** * asymmetric_match_key_ids - Search asymmetric key IDs * @kids: The list of key IDs to check * @match_id: The key ID we're looking for + * @match: The match function to use */ -bool asymmetric_match_key_ids(const struct asymmetric_key_ids *kids, - const struct asymmetric_key_id *match_id) +static bool asymmetric_match_key_ids( + const struct asymmetric_key_ids *kids, + const struct asymmetric_key_id *match_id, + bool (*match)(const struct asymmetric_key_id *kid1, + const struct asymmetric_key_id *kid2)) { + int i; + if (!kids || !match_id) return false; - if (asymmetric_key_id_same(kids->id[0], match_id)) - return true; - if (asymmetric_key_id_same(kids->id[1], match_id)) - return true; + for (i = 0; i < ARRAY_SIZE(kids->id); i++) + if (match(kids->id[i], match_id)) + return true; return false; } -EXPORT_SYMBOL_GPL(asymmetric_match_key_ids); /** * asymmetric_key_hex_to_key_id - Convert a hex string into a key ID. @@ -113,7 +134,7 @@ struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id) } /* - * Match asymmetric keys by ID. + * Match asymmetric keys by an exact match on an ID. */ static bool asymmetric_key_cmp(const struct key *key, const struct key_match_data *match_data) @@ -121,7 +142,21 @@ static bool asymmetric_key_cmp(const struct key *key, const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); const struct asymmetric_key_id *match_id = match_data->preparsed; - return asymmetric_match_key_ids(kids, match_id); + return asymmetric_match_key_ids(kids, match_id, + asymmetric_key_id_same); +} + +/* + * Match asymmetric keys by a partial match on an IDs. + */ +static bool asymmetric_key_cmp_partial(const struct key *key, + const struct key_match_data *match_data) +{ + const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); + const struct asymmetric_key_id *match_id = match_data->preparsed; + + return asymmetric_match_key_ids(kids, match_id, + asymmetric_key_id_partial); } /* @@ -131,7 +166,8 @@ static bool asymmetric_key_cmp(const struct key *key, * There are some specifiers for matching key IDs rather than by the key * description: * - * "id:" - request a key by any available ID + * "id:" - find a key by partial match on any available ID + * "ex:" - find a key by exact match on any available ID * * These have to be searched by iteration rather than by direct lookup because * the key is hashed according to its description. @@ -141,6 +177,8 @@ static int asymmetric_key_match_preparse(struct key_match_data *match_data) struct asymmetric_key_id *match_id; const char *spec = match_data->raw_data; const char *id; + bool (*cmp)(const struct key *, const struct key_match_data *) = + asymmetric_key_cmp; if (!spec || !*spec) return -EINVAL; @@ -148,6 +186,11 @@ static int asymmetric_key_match_preparse(struct key_match_data *match_data) spec[1] == 'd' && spec[2] == ':') { id = spec + 3; + cmp = asymmetric_key_cmp_partial; + } else if (spec[0] == 'e' && + spec[1] == 'x' && + spec[2] == ':') { + id = spec + 3; } else { goto default_match; } @@ -157,7 +200,7 @@ static int asymmetric_key_match_preparse(struct key_match_data *match_data) return PTR_ERR(match_id); match_data->preparsed = match_id; - match_data->cmp = asymmetric_key_cmp; + match_data->cmp = cmp; match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; return 0; @@ -251,6 +294,7 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) { struct asymmetric_key_subtype *subtype = prep->type_data[0]; struct asymmetric_key_ids *kids = prep->type_data[1]; + int i; pr_devel("==>%s()\n", __func__); @@ -259,8 +303,8 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) module_put(subtype->owner); } if (kids) { - kfree(kids->id[0]); - kfree(kids->id[1]); + for (i = 0; i < ARRAY_SIZE(kids->id); i++) + kfree(kids->id[i]); kfree(kids); } kfree(prep->description); diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index ae47be6128c4..1d29376072da 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -54,7 +54,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, /* Look to see if this certificate is present in the trusted * keys. */ - key = x509_request_asymmetric_key(trust_keyring, x509->id); + key = x509_request_asymmetric_key(trust_keyring, x509->id, + false); if (!IS_ERR(key)) { /* One of the X.509 certificates in the PKCS#7 message * is apparently the same as one we already trust. @@ -85,7 +86,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * trusted keys. */ if (last && last->authority) { - key = x509_request_asymmetric_key(trust_keyring, last->authority); + key = x509_request_asymmetric_key(trust_keyring, last->authority, + false); if (!IS_ERR(key)) { x509 = last; pr_devel("sinfo %u: Root cert %u signer is key %x\n", @@ -100,7 +102,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * the signed info directly. */ key = x509_request_asymmetric_key(trust_keyring, - sinfo->signing_cert_id); + sinfo->signing_cert_id, + false); if (!IS_ERR(key)) { pr_devel("sinfo %u: Direct signer is key %x\n", sinfo->index, key_serial(key)); diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 8bffb06b2683..6ef54495be87 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -53,13 +53,15 @@ __setup("ca_keys=", ca_keys_setup); * x509_request_asymmetric_key - Request a key by X.509 certificate params. * @keyring: The keys to search. * @kid: The key ID. + * @partial: Use partial match if true, exact if false. * * Find a key in the given keyring by subject name and key ID. These might, * for instance, be the issuer name and the authority key ID of an X.509 * certificate that needs to be verified. */ struct key *x509_request_asymmetric_key(struct key *keyring, - const struct asymmetric_key_id *kid) + const struct asymmetric_key_id *kid, + bool partial) { key_ref_t key; char *id, *p; @@ -69,8 +71,13 @@ struct key *x509_request_asymmetric_key(struct key *keyring, if (!id) return ERR_PTR(-ENOMEM); - *p++ = 'i'; - *p++ = 'd'; + if (partial) { + *p++ = 'i'; + *p++ = 'd'; + } else { + *p++ = 'e'; + *p++ = 'x'; + } *p++ = ':'; p = bin2hex(p, kid->data, kid->len); *p = 0; @@ -207,10 +214,11 @@ static int x509_validate_trust(struct x509_certificate *cert, if (!trust_keyring) return -EOPNOTSUPP; - if (ca_keyid && !asymmetric_key_id_same(cert->authority, ca_keyid)) + if (ca_keyid && !asymmetric_key_id_partial(cert->authority, ca_keyid)) return -EPERM; - key = x509_request_asymmetric_key(trust_keyring, cert->authority); + key = x509_request_asymmetric_key(trust_keyring, cert->authority, + false); if (!IS_ERR(key)) { if (!use_builtin_keys || test_bit(KEY_FLAG_BUILTIN, &key->flags)) diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index fa73a6fd536c..54add2069901 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -101,6 +101,7 @@ extern int verify_signature(const struct key *key, struct asymmetric_key_id; extern struct key *x509_request_asymmetric_key(struct key *keyring, - const struct asymmetric_key_id *kid); + const struct asymmetric_key_id *kid, + bool partial); #endif /* _LINUX_PUBLIC_KEY_H */ diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h index 044ab0d3aa45..c0754abb2f56 100644 --- a/include/keys/asymmetric-type.h +++ b/include/keys/asymmetric-type.h @@ -51,6 +51,9 @@ struct asymmetric_key_ids { extern bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1, const struct asymmetric_key_id *kid2); +extern bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1, + const struct asymmetric_key_id *kid2); + extern struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1, size_t len_1, const void *val_2, -- cgit v1.2.3 From 8dd609805b87923a700a2fad646390a58013cdb9 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 6 Oct 2014 16:52:12 +0100 Subject: KEYS: use swapped SKID for performing partial matching Earlier KEYS code used pure subject key identifiers (fingerprint) for searching keys. Latest merged code removed that and broke compatibility with integrity subsytem signatures and original format of module signatures. This patch returns back partial matching on SKID. Reported-by: Dmitry Kasatkin Signed-off-by: Dmitry Kasatkin Signed-off-by: David Howells --- crypto/asymmetric_keys/x509_cert_parser.c | 12 ++++++------ crypto/asymmetric_keys/x509_parser.h | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 393706f33fa5..a668d90302d3 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -437,9 +437,9 @@ int x509_process_extension(void *context, size_t hdrlen, ctx->cert->raw_skid_size = vlen; ctx->cert->raw_skid = v; - kid = asymmetric_key_generate_id(v, vlen, - ctx->cert->raw_subject, - ctx->cert->raw_subject_size); + kid = asymmetric_key_generate_id(ctx->cert->raw_subject, + ctx->cert->raw_subject_size, + v, vlen); if (IS_ERR(kid)) return PTR_ERR(kid); ctx->cert->skid = kid; @@ -493,9 +493,9 @@ int x509_process_extension(void *context, size_t hdrlen, v += (sub + 2); } - kid = asymmetric_key_generate_id(v, vlen, - ctx->cert->raw_issuer, - ctx->cert->raw_issuer_size); + kid = asymmetric_key_generate_id(ctx->cert->raw_issuer, + ctx->cert->raw_issuer_size, + v, vlen); if (IS_ERR(kid)) return PTR_ERR(kid); pr_debug("authkeyid %*phN\n", kid->len, kid->data); diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index 3f0f0f081621..3dfe6b5d6f0b 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -19,9 +19,9 @@ struct x509_certificate { struct public_key_signature sig; /* Signature parameters */ char *issuer; /* Name of certificate issuer */ char *subject; /* Name of certificate subject */ - struct asymmetric_key_id *id; /* Issuer + serial number */ - struct asymmetric_key_id *skid; /* Subject key identifier */ - struct asymmetric_key_id *authority; /* Authority key identifier */ + struct asymmetric_key_id *id; /* Serial number + issuer */ + struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */ + struct asymmetric_key_id *authority; /* Authority key identifier (optional) */ struct tm valid_from; struct tm valid_to; const void *tbs; /* Signed data */ -- cgit v1.2.3 From 7a224e783ad2b325977253139aede1f5f38b1778 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 6 Oct 2014 17:02:32 +0100 Subject: KEYS: strip 'id:' from ca_keyid The 'id:' prefix must be stripped for asymmetric_key_hex_to_key_id() to be able to process ca_keyid. Signed-off-by: Dmitry Kasatkin Signed-off-by: David Howells --- crypto/asymmetric_keys/x509_public_key.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 6ef54495be87..a6c42031628e 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -35,7 +35,7 @@ static int __init ca_keys_setup(char *str) if (strncmp(str, "id:", 3) == 0) { struct asymmetric_key_id *p; - p = asymmetric_key_hex_to_key_id(str); + p = asymmetric_key_hex_to_key_id(str + 3); if (p == ERR_PTR(-EINVAL)) pr_err("Unparsable hex string in ca_keys\n"); else if (!IS_ERR(p)) -- cgit v1.2.3 From d4016589855156b6afe5e9bc8ef85ce43f27e704 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 6 Oct 2014 17:25:45 +0100 Subject: KEYS: output last portion of fingerprint in /proc/keys Previous version of KEYS used to output last 4 bytes of fingerprint. Now it outputs 8 last bytes of raw subject, which does not make any visual meaning at all. This patch restores old behavior. Signed-off-by: Dmitry Kasatkin Signed-off-by: David Howells --- crypto/asymmetric_keys/asymmetric_type.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 052e944bb109..bcbbbd794e1d 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -233,14 +233,14 @@ static void asymmetric_key_describe(const struct key *key, struct seq_file *m) seq_puts(m, ": "); subtype->describe(key, m); - if (kids && kids->id[0]) { - kid = kids->id[0]; + if (kids && kids->id[1]) { + kid = kids->id[1]; seq_putc(m, ' '); n = kid->len; p = kid->data; - if (n > 8) { - p += n - 8; - n = 8; + if (n > 4) { + p += n - 4; + n = 4; } seq_printf(m, "%*phN", n, p); } -- cgit v1.2.3 From 594081ee7145cc30a3977cb4e218f81213b63dc5 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 6 Oct 2014 17:31:58 +0100 Subject: integrity: do zero padding of the key id Latest KEYS code return error if hexadecimal string length id odd. Fix it. Signed-off-by: Dmitry Kasatkin Signed-off-by: David Howells --- security/integrity/digsig_asymmetric.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 37e0d98517a8..4fec1816a2b3 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -28,7 +28,7 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) struct key *key; char name[12]; - sprintf(name, "id:%x", keyid); + sprintf(name, "id:%08x", keyid); pr_debug("key search: \"%s\"\n", name); -- cgit v1.2.3