From patchwork Fri Aug 21 18:21:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tushar Sugandhi X-Patchwork-Id: 11730193 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 98245739 for ; Fri, 21 Aug 2020 18:21:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7A92620720 for ; Fri, 21 Aug 2020 18:21:48 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b="EMHAgV5I" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726599AbgHUSVo (ORCPT ); Fri, 21 Aug 2020 14:21:44 -0400 Received: from linux.microsoft.com ([13.77.154.182]:42958 "EHLO linux.microsoft.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725831AbgHUSVV (ORCPT ); Fri, 21 Aug 2020 14:21:21 -0400 Received: from tusharsu-Ubuntu.lan (c-71-197-163-6.hsd1.wa.comcast.net [71.197.163.6]) by linux.microsoft.com (Postfix) with ESMTPSA id 7A7D020B490F; Fri, 21 Aug 2020 11:21:18 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 7A7D020B490F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1598034079; bh=uWkHzR0ZcqR9l/na6Wym14QYTHMQuhcrOfLOMA/iGiQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EMHAgV5IDY/3C0x3ia5Y0d2ylArOVJMHBaUlGWXWEhro5sJboXYUje3LtFyw00XjO dbHk02UU4JnJMtjcCe34K9mMC9SiwDjR+a3lU38Topi7SEQAq0cpTeAG/UQ5sJepsM INKitX4QfjSO3IF/t4+VvsC46+8eABshRtl9YYdw= From: Tushar Sugandhi To: zohar@linux.ibm.com, stephen.smalley.work@gmail.com, casey@schaufler-ca.com, agk@redhat.com, snitzer@redhat.com, gmazyland@gmail.com Cc: tyhicks@linux.microsoft.com, sashal@kernel.org, jmorris@namei.org, nramas@linux.microsoft.com, linux-integrity@vger.kernel.org, selinux@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, dm-devel@redhat.com Subject: [PATCH v2 3/3] IMA: define IMA hook to measure critical data from kernel components Date: Fri, 21 Aug 2020 11:21:07 -0700 Message-Id: <20200821182107.5328-4-tusharsu@linux.microsoft.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200821182107.5328-1-tusharsu@linux.microsoft.com> References: <20200821182107.5328-1-tusharsu@linux.microsoft.com> Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org Currently, IMA does not provide a generic function to kernel components to measure their data. A generic function provided by IMA would enable various parts of the kernel with easier and faster on-boarding to use IMA infrastructure, would avoid code duplication, and consistent usage of IMA policy CRITICAL_DATA+data_sources across the kernel. Define a generic IMA function ima_measure_critical_data() to measure data from various kernel components. Limit the measurement to the components that are specified in the IMA policy - CRITICAL_DATA+data_sources. Update process_buffer_measurement() to return the status code of the operation. Introduce a boolean parameter measure_buf_hash to support measuring hash of a buffer, instead of the buffer itself. This is useful when the buffer being measured is too large, which may result in bloated IMA logs. Signed-off-by: Tushar Sugandhi --- include/linux/ima.h | 11 ++++ security/integrity/ima/ima.h | 7 ++- security/integrity/ima/ima_appraise.c | 2 +- security/integrity/ima/ima_asymmetric_keys.c | 2 +- security/integrity/ima/ima_main.c | 65 +++++++++++++++++--- security/integrity/ima/ima_queue_keys.c | 3 +- 6 files changed, 75 insertions(+), 15 deletions(-) diff --git a/include/linux/ima.h b/include/linux/ima.h index d15100de6cdd..136fc02580db 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -26,6 +26,10 @@ extern int ima_post_read_file(struct file *file, void *buf, loff_t size, extern void ima_post_path_mknod(struct dentry *dentry); extern int ima_file_hash(struct file *file, char *buf, size_t buf_size); extern void ima_kexec_cmdline(int kernel_fd, const void *buf, int size); +extern int ima_measure_critical_data(const char *event_name, + const char *event_data_source, + const void *buf, int buf_len, + bool measure_buf_hash); #ifdef CONFIG_IMA_KEXEC extern void ima_add_kexec_buffer(struct kimage *image); @@ -104,6 +108,13 @@ static inline int ima_file_hash(struct file *file, char *buf, size_t buf_size) } static inline void ima_kexec_cmdline(int kernel_fd, const void *buf, int size) {} +static inline int ima_measure_critical_data(const char *event_name, + const char *event_data_source, + const void *buf, int buf_len, + bool measure_buf_hash) +{ + return -EOPNOTSUPP; +} #endif /* CONFIG_IMA */ #ifndef CONFIG_IMA_KEXEC diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 0f4209a92bfb..00b84052c8f1 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -266,9 +266,10 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, struct evm_ima_xattr_data *xattr_value, int xattr_len, const struct modsig *modsig, int pcr, struct ima_template_desc *template_desc); -void process_buffer_measurement(struct inode *inode, const void *buf, int size, - const char *eventname, enum ima_hooks func, - int pcr, const char *func_data); +int process_buffer_measurement(struct inode *inode, const void *buf, int size, + const char *eventname, enum ima_hooks func, + int pcr, const char *func_data, + bool measure_buf_hash); void ima_audit_measurement(struct integrity_iint_cache *iint, const unsigned char *filename); int ima_alloc_init_template(struct ima_event_data *event_data, diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 372d16382960..20adffe5bf58 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -336,7 +336,7 @@ int ima_check_blacklist(struct integrity_iint_cache *iint, if ((rc == -EPERM) && (iint->flags & IMA_MEASURE)) process_buffer_measurement(NULL, digest, digestsize, "blacklisted-hash", NONE, - pcr, NULL); + pcr, NULL, false); } return rc; diff --git a/security/integrity/ima/ima_asymmetric_keys.c b/security/integrity/ima/ima_asymmetric_keys.c index 1c68c500c26f..a74095793936 100644 --- a/security/integrity/ima/ima_asymmetric_keys.c +++ b/security/integrity/ima/ima_asymmetric_keys.c @@ -60,5 +60,5 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key, */ process_buffer_measurement(NULL, payload, payload_len, keyring->description, KEY_CHECK, 0, - keyring->description); + keyring->description, false); } diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index c870fd6d2f83..a889bf40cb7e 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -733,17 +733,21 @@ int ima_load_data(enum kernel_load_data_id id) * @func: IMA hook * @pcr: pcr to extend the measurement * @func_data: private data specific to @func, can be NULL. + * @measure_buf_hash: if set to true - will measure hash of the buf, + * instead of buf * * Based on policy, the buffer is measured into the ima log. */ -void process_buffer_measurement(struct inode *inode, const void *buf, int size, - const char *eventname, enum ima_hooks func, - int pcr, const char *func_data) +int process_buffer_measurement(struct inode *inode, const void *buf, int size, + const char *eventname, enum ima_hooks func, + int pcr, const char *func_data, + bool measure_buf_hash) { int ret = 0; const char *audit_cause = "ENOMEM"; struct ima_template_entry *entry = NULL; struct integrity_iint_cache iint = {}; + struct integrity_iint_cache digest_iint = {}; struct ima_event_data event_data = {.iint = &iint, .filename = eventname, .buf = buf, @@ -752,13 +756,13 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size, struct { struct ima_digest_data hdr; char digest[IMA_MAX_DIGEST_SIZE]; - } hash = {}; + } hash = {}, digest_hash = {}; int violation = 0; int action = 0; u32 secid; if (!ima_policy_flag) - return; + return 0; /* * Both LSM hooks and auxilary based buffer measurements are @@ -772,7 +776,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size, action = ima_get_action(inode, current_cred(), secid, 0, func, &pcr, &template, func_data); if (!(action & IMA_MEASURE)) - return; + return 0; } if (!pcr) @@ -787,7 +791,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size, pr_err("template %s init failed, result: %d\n", (strlen(template->name) ? template->name : template->fmt), ret); - return; + return ret; } } @@ -801,6 +805,24 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size, goto out; } + if (measure_buf_hash) { + digest_iint.ima_hash = &digest_hash.hdr; + digest_iint.ima_hash->algo = ima_hash_algo; + digest_iint.ima_hash->length = hash_digest_size[ima_hash_algo]; + + ret = ima_calc_buffer_hash(hash.hdr.digest, + iint.ima_hash->length, + digest_iint.ima_hash); + if (ret < 0) { + audit_cause = "digest_hashing_error"; + goto out; + } + + event_data.iint = &digest_iint; + event_data.buf = hash.hdr.digest; + event_data.buf_len = iint.ima_hash->length; + } + ret = ima_alloc_init_template(&event_data, &entry, template); if (ret < 0) { audit_cause = "alloc_entry"; @@ -819,7 +841,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size, func_measure_str(func), audit_cause, ret, 0, ret); - return; + return ret; } /** @@ -842,10 +864,35 @@ void ima_kexec_cmdline(int kernel_fd, const void *buf, int size) return; process_buffer_measurement(file_inode(f.file), buf, size, - "kexec-cmdline", KEXEC_CMDLINE, 0, NULL); + "kexec-cmdline", KEXEC_CMDLINE, 0, NULL, + false); fdput(f); } +/** + * ima_measure_critical_data - measure critical data + * @event_name: name for the given data + * @event_data_source: name of the event data source + * @buf: pointer to buffer containing data to measure + * @buf_len: length of buffer(in bytes) + * @measure_buf_hash: if set to true - will measure hash of the buf, + * instead of buf + * + * Buffers can only be measured, not appraised. + */ +int ima_measure_critical_data(const char *event_name, + const char *event_data_source, + const void *buf, int buf_len, + bool measure_buf_hash) +{ + if (!event_name || !event_data_source || !buf || !buf_len) + return -EINVAL; + + return process_buffer_measurement(NULL, buf, buf_len, event_name, + CRITICAL_DATA, 0, event_data_source, + measure_buf_hash); +} + static int __init init_ima(void) { int error; diff --git a/security/integrity/ima/ima_queue_keys.c b/security/integrity/ima/ima_queue_keys.c index 69a8626a35c0..c2f2ad34f9b7 100644 --- a/security/integrity/ima/ima_queue_keys.c +++ b/security/integrity/ima/ima_queue_keys.c @@ -162,7 +162,8 @@ void ima_process_queued_keys(void) entry->payload_len, entry->keyring_name, KEY_CHECK, 0, - entry->keyring_name); + entry->keyring_name, + false); list_del(&entry->list); ima_free_key_entry(entry); }