On Fri, 2019-09-27 at 10:25 -0400, Nayna Jain wrote: > Asymmetric private keys are used to sign multiple files. The kernel > currently support checking against the blacklisted keys. However, if the > public key is blacklisted, any file signed by the blacklisted key will > automatically fail signature verification. We might not want to blacklist > all the files signed by a particular key, but just a single file. > Blacklisting the public key is not fine enough granularity. > > This patch adds support for blacklisting binaries with appended signatures, > based on the IMA policy. Defined is a new policy option > "appraise_flag=check_blacklist". > > Signed-off-by: Nayna Jain <nayna@xxxxxxxxxxxxx> > --- > Documentation/ABI/testing/ima_policy | 1 + > security/integrity/ima/ima.h | 12 +++++++++ > security/integrity/ima/ima_appraise.c | 35 +++++++++++++++++++++++++++ > security/integrity/ima/ima_main.c | 8 ++++-- > security/integrity/ima/ima_policy.c | 10 ++++++-- > security/integrity/integrity.h | 1 + > 6 files changed, 63 insertions(+), 4 deletions(-) > > diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy > index 29ebe9afdac4..4c97afcc0f3c 100644 > --- a/Documentation/ABI/testing/ima_policy > +++ b/Documentation/ABI/testing/ima_policy > @@ -25,6 +25,7 @@ Description: > lsm: [[subj_user=] [subj_role=] [subj_type=] > [obj_user=] [obj_role=] [obj_type=]] > option: [[appraise_type=]] [template=] [permit_directio] > + [appraise_flag=[check_blacklist]] > base: func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK] > [FIRMWARE_CHECK] > [KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK] > diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h > index 9bf509217e8e..2c034728b239 100644 > --- a/security/integrity/ima/ima.h > +++ b/security/integrity/ima/ima.h > @@ -254,6 +254,9 @@ int ima_policy_show(struct seq_file *m, void *v); > #define IMA_APPRAISE_KEXEC 0x40 > > #ifdef CONFIG_IMA_APPRAISE > +int ima_blacklist_measurement(struct integrity_iint_cache *iint, > + const struct modsig *modsig, int action, > + int pcr, struct ima_template_desc *template_desc); > int ima_appraise_measurement(enum ima_hooks func, > struct integrity_iint_cache *iint, > struct file *file, const unsigned char *filename, > @@ -269,6 +272,15 @@ int ima_read_xattr(struct dentry *dentry, > struct evm_ima_xattr_data **xattr_value); > > #else > +static inline int ima_blacklist_measurement(struct integrity_iint_cache *iint, > + const struct modsig *modsig, > + int action, int pcr, > + struct ima_template_desc > + *template_desc) > +{ > + return 0; > +} > + > static inline int ima_appraise_measurement(enum ima_hooks func, > struct integrity_iint_cache *iint, > struct file *file, > diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c > index 136ae4e0ee92..a5a82e870e24 100644 > --- a/security/integrity/ima/ima_appraise.c > +++ b/security/integrity/ima/ima_appraise.c > @@ -12,6 +12,7 @@ > #include <linux/magic.h> > #include <linux/ima.h> > #include <linux/evm.h> > +#include <keys/system_keyring.h> > > #include "ima.h" > > @@ -303,6 +304,40 @@ static int modsig_verify(enum ima_hooks func, const struct modsig *modsig, > return rc; > } > > +/* > + * ima_blacklist_measurement - checks if the file measurement is blacklisted > + * > + * Returns -EKEYREJECTED if the hash is blacklisted. > + */ In the function comment, please mention that blacklisted binaries are included in the IMA measurement list. > +int ima_blacklist_measurement(struct integrity_iint_cache *iint, > + const struct modsig *modsig, int action, int pcr, > + struct ima_template_desc *template_desc) > +{ > + enum hash_algo hash_algo; > + const u8 *digest = NULL; > + u32 digestsize = 0; > + u32 secid; > + int rc = 0; > + > + if (!(iint->flags & IMA_CHECK_BLACKLIST)) > + return 0; > + > + if (iint->flags & IMA_MODSIG_ALLOWED) { > + security_task_getsecid(current, &secid); > + ima_get_modsig_digest(modsig, &hash_algo, &digest, &digestsize); > + > + rc = is_hash_blacklisted(digest, digestsize, "bin"); > + > + /* Returns -EKEYREJECTED on blacklisted hash found */ is_hash_blacklisted() returning -EKEYREJECTED makes sense if the key is blacklisted, not so much for a binary. It would make more sense to define is_binary_blacklisted(), as a wrapper for is_hash_blacklisted(). > + if ((rc == -EKEYREJECTED) && (iint->flags & IMA_MEASURE)) > + process_buffer_measurement(digest, digestsize, > + "blacklisted-hash", pcr, > + template_desc); For appended signatures, the IMA policy measurement rule would normally be "template=ima-modsig". Shouldn't the "template_desc" for blacklisted binaries be "template=ima-buf"? > + } > + > + return rc; > +} > + > /* > * ima_appraise_measurement - appraise file measurement > * > diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c > index ae0c1bdc4eaf..92c446045637 100644 > --- a/security/integrity/ima/ima_main.c > +++ b/security/integrity/ima/ima_main.c > @@ -336,8 +336,12 @@ static int process_measurement(struct file *file, const struct cred *cred, > template_desc); > if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) { > inode_lock(inode); The lock is needed for updating the extended attributes (in IMA fix mode). Please limit taking the lock to ima_appraise_measurement(). > - rc = ima_appraise_measurement(func, iint, file, pathname, > - xattr_value, xattr_len, modsig); > + rc = ima_blacklist_measurement(iint, modsig, action, pcr, > + template_desc); process_measurement() calls a number of functions: ima_collect_measurement(), ima_store_measurement(), ima_appraise_measurement() and ima_audit_measurement(). The action is contained within the name (eg. collect, store, appraise, audit). "blacklist" implies the function is blacklisting a file hash, as opposed to checking whether the file hash is already black listed. Changing the tense from "blacklist" to "blacklisted" would help. Renaming the function to "ima_is_binary_blacklisted" would be even better. Mimi > + if (rc != -EKEYREJECTED) > + rc = ima_appraise_measurement(func, iint, file, > + pathname, xattr_value, > + xattr_len, modsig); > inode_unlock(inode); > if (!rc) > rc = mmap_violation_check(func, file, &pathbuf, > diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c > index 4badc4fcda98..ad3b3af69460 100644 > --- a/security/integrity/ima/ima_policy.c > +++ b/security/integrity/ima/ima_policy.c > @@ -765,8 +765,8 @@ enum { > Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq, > Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt, > Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt, > - Opt_appraise_type, Opt_permit_directio, > - Opt_pcr, Opt_template, Opt_err > + Opt_appraise_type, Opt_appraise_flag, > + Opt_permit_directio, Opt_pcr, Opt_template, Opt_err > }; > > static const match_table_t policy_tokens = { > @@ -798,6 +798,7 @@ static const match_table_t policy_tokens = { > {Opt_euid_lt, "euid<%s"}, > {Opt_fowner_lt, "fowner<%s"}, > {Opt_appraise_type, "appraise_type=%s"}, > + {Opt_appraise_flag, "appraise_flag=%s"}, > {Opt_permit_directio, "permit_directio"}, > {Opt_pcr, "pcr=%s"}, > {Opt_template, "template=%s"}, > @@ -1172,6 +1173,11 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) > else > result = -EINVAL; > break; > + case Opt_appraise_flag: > + ima_log_string(ab, "appraise_flag", args[0].from); > + if (strstr(args[0].from, "blacklist")) > + entry->flags |= IMA_CHECK_BLACKLIST; > + break; > case Opt_permit_directio: > entry->flags |= IMA_PERMIT_DIRECTIO; > break; > diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h > index d9323d31a3a8..73fc286834d7 100644 > --- a/security/integrity/integrity.h > +++ b/security/integrity/integrity.h > @@ -32,6 +32,7 @@ > #define EVM_IMMUTABLE_DIGSIG 0x08000000 > #define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000 > #define IMA_MODSIG_ALLOWED 0x20000000 > +#define IMA_CHECK_BLACKLIST 0x40000000 > > #define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \ > IMA_HASH | IMA_APPRAISE_SUBMASK)