From patchwork Mon Oct 19 17:26:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tushar Sugandhi X-Patchwork-Id: 11846021 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0167CC433DF for ; Tue, 20 Oct 2020 08:22:28 +0000 (UTC) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 2D8DD2222F for ; Tue, 20 Oct 2020 08:22:26 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 2D8DD2222F Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: mail.kernel.org; spf=tempfail smtp.mailfrom=dm-devel-bounces@redhat.com Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-417-EJtnHdDyNoq31Iq6iypt4w-1; Tue, 20 Oct 2020 04:22:23 -0400 X-MC-Unique: EJtnHdDyNoq31Iq6iypt4w-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 7EFED8049D7; Tue, 20 Oct 2020 08:22:17 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 7F2BC5C225; Tue, 20 Oct 2020 08:22:15 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id DAA561826D35; Tue, 20 Oct 2020 08:22:11 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 09JHQWvD012595 for ; Mon, 19 Oct 2020 13:26:32 -0400 Received: by smtp.corp.redhat.com (Postfix) id EC962202278F; Mon, 19 Oct 2020 17:26:31 +0000 (UTC) Received: from mimecast-mx02.redhat.com (mimecast06.extmail.prod.ext.rdu2.redhat.com [10.11.55.22]) by smtp.corp.redhat.com (Postfix) with ESMTPS id E796B2022780 for ; Mon, 19 Oct 2020 17:26:28 +0000 (UTC) Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [205.139.110.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id E4D51182360B for ; Mon, 19 Oct 2020 17:26:27 +0000 (UTC) Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by relay.mimecast.com with ESMTP id us-mta-284-vk1mnfpNNi60v3BUjbHZeQ-1; Mon, 19 Oct 2020 13:26:25 -0400 X-MC-Unique: vk1mnfpNNi60v3BUjbHZeQ-1 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 C683320B4907; Mon, 19 Oct 2020 10:26:23 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com C683320B4907 From: Tushar Sugandhi To: zohar@linux.ibm.com, agk@redhat.com, snitzer@redhat.com, gmazyland@gmail.com Date: Mon, 19 Oct 2020 10:26:06 -0700 Message-Id: <20201019172607.16714-2-tusharsu@linux.microsoft.com> In-Reply-To: <20201019172607.16714-1-tusharsu@linux.microsoft.com> References: <20201019172607.16714-1-tusharsu@linux.microsoft.com> X-Mimecast-Impersonation-Protect: Policy=CLT - Impersonation Protection Definition; Similar Internal Domain=false; Similar Monitored External Domain=false; Custom External Domain=false; Mimecast External Domain=false; Newly Observed Domain=false; Internal User Name=false; Custom Display Name List=false; Reply-to Address Mismatch=false; Targeted Threat Dictionary=false; Mimecast Threat Dictionary=false; Custom Threat Dictionary=false X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-loop: dm-devel@redhat.com X-Mailman-Approved-At: Tue, 20 Oct 2020 04:22:11 -0400 Cc: sashal@kernel.org, jmorris@namei.org, linux-kernel@vger.kernel.org, nramas@linux.microsoft.com, dm-devel@redhat.com, tyhicks@linux.microsoft.com, linux-integrity@vger.kernel.org Subject: [dm-devel] [PATCH v4 1/2] dm-devel: collect target data and submit to IMA to measure X-BeenThere: dm-devel@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: device-mapper development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: dm-devel-bounces@redhat.com Errors-To: dm-devel-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=dm-devel-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com For the device-mapper targets to take advantage of IMA's measuring and quoting abilities, and for enabling remote attestation for device-mapper targets, device-mapper needs to provide the functionality to consistently measure target data using ima_measure_critical_data() function provided by IMA. A generic set of functions at device-mapper layer would enable the measurement structure to be uniform across targets, and avoid code duplication. It will also make on-boarding easier and faster for targets that want to use IMA infrastructure for measurements, quoting, and remote attestation. The uniform measurement structure across targets would also help the remote attestation services to consistently process, across targets, the measurement to be attested. Implement a set of functions at device-mapper layer to manage the data coming from various device-mapper targets to be measured by IMA. Provide the functionality for various tasks - initialize the necessary data structures, add the data to the list of key-value pairs to be be measured, reset the list if needed (e.g. in error/retry cases), and finally pass it on to device-mapper layer, to be measured by IMA. Ensure the functionality is generic and implemented at device-mapper layer, so that any device-mapper target can use it to measure its data through IMA. Also make sure the functionality is non-intrusive/best effort for the targets using it. The errors in managing the list to be measured, and the actual errors in the measurement should not disrupt the core functionality of the targets. Protect the list of key value pairs to be measured for a given target, by putting it under critical sections - so that multi-threaded targets can safely use the list to append the data from different threads, for measurements and quoting. Compute the last measurement's hash and store it internally, so that unnecessary duplicate data is not sent to IMA for measurement. Divide the functionality into 5 main functions: (1) dm_ima_init_measurements(): Use it to initialize device-mapper target's IMA measurement list. It should abstract the necessary data initialization from the device-mapper target apps. (2) dm_ima_append_measurement_list(): Use it to append the key-value pair to the existing list of key-value pairs to measure. (3) dm_ima_finalize_and_measure(): Use it to measure the key-value pair list for a given target, and finally release the resources held by the list for that specific target. Note that the data given by the target for a given device would be sent to IMA subsystem for measurement only if it has changed since the last time it was measured. (4) dm_ima_reset_measurement_list(): Use it to reset device-mapper target's ima measurement list, by releasing the resources held by the list. Use it if the measurements list need to be reset after dm_ima_init_measurements() and before calling dm_ima_finalize_and_measure(). This can be needed in scenarios like recovering from error paths and retrying measurements and quoting again. (5) dm_ima_exit_measurements(): Use it during the destruction of the target - to release the resources held for measurement. This is useful to protect the kernel from possible resource leaks when the target adds data for measurements using dm_ima_append_measurement_list(), but gets destroyed before calling dm_ima_finalize_and_measure(). Signed-off-by: Tushar Sugandhi --- block/genhd.c | 2 + drivers/md/Makefile | 1 + drivers/md/dm-ima.c | 378 ++++++++++++++++++++++++++++++++++ include/linux/device-mapper.h | 67 ++++++ include/linux/genhd.h | 1 + 5 files changed, 449 insertions(+) create mode 100644 drivers/md/dm-ima.c diff --git a/block/genhd.c b/block/genhd.c index 99c64641c314..2afee164126f 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -83,6 +83,7 @@ char *disk_name(struct gendisk *hd, int partno, char *buf) return buf; } +EXPORT_SYMBOL(disk_name); const char *bdevname(struct block_device *bdev, char *buf) { @@ -1035,6 +1036,7 @@ struct gendisk *get_gendisk(dev_t devt, int *partno) } return disk; } +EXPORT_SYMBOL(get_gendisk); /** * bdget_disk - do bdget() by gendisk and partition number diff --git a/drivers/md/Makefile b/drivers/md/Makefile index 6d3e234dc46a..0dc50181aaf2 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_DM_LOG_WRITES) += dm-log-writes.o obj-$(CONFIG_DM_INTEGRITY) += dm-integrity.o obj-$(CONFIG_DM_ZONED) += dm-zoned.o obj-$(CONFIG_DM_WRITECACHE) += dm-writecache.o +obj-$(CONFIG_IMA) += dm-ima.o ifeq ($(CONFIG_DM_INIT),y) dm-mod-objs += dm-init.o diff --git a/drivers/md/dm-ima.c b/drivers/md/dm-ima.c new file mode 100644 index 000000000000..f905070b7497 --- /dev/null +++ b/drivers/md/dm-ima.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Microsoft Corporation + * + * Author: Tushar Sugandhi + * + * File: dm-ima.c + * Enables IMA measurements for DM targets + */ + +#include "dm-core.h" + +#include +#include +#include +#include +#include +#include +#include + +#define DM_MSG_PREFIX "ima" + +static int dm_compute_buffer_hash(void *buf, + size_t buf_len, + void **buf_hash, + int *buf_hash_len) +{ + struct crypto_shash *tfm; + struct shash_desc *desc = NULL; + void *digest = NULL; + int desc_size; + int digest_size; + int ret = 0; + + tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + + digest = kmalloc(digest_size, GFP_KERNEL); + if (!digest) { + ret = -ENOMEM; + goto error; + } + + desc = kzalloc(desc_size, GFP_KERNEL); + if (!desc) { + ret = -ENOMEM; + goto error; + } + + desc->tfm = tfm; + + ret = crypto_shash_digest(desc, buf, buf_len, digest); + if (ret < 0) + goto error; + + *buf_hash_len = digest_size; + *buf_hash = digest; + digest = NULL; + +error: + kfree(desc); + kfree(digest); + + crypto_free_shash(tfm); + + return ret; +} + +static void dm_release_ima_measurements(struct list_head + *ima_kv_list) +{ + struct ima_keyval *cur_keyval, *tmp_keyval; + + list_for_each_entry_safe(cur_keyval, + tmp_keyval, + ima_kv_list, + kv_list) { + + list_del(&cur_keyval->kv_list); + kzfree(cur_keyval->key); + kzfree(cur_keyval->val); + kfree(cur_keyval); + } +} +/* + * Get device name from major:minor + */ +int get_devname_from_maj_min(const char *ip_dev_maj_min, char **op_dev_name) +{ + int maj, min, part, len, i, maj_min_count = 0, r = 0; + char *l_dev_maj_min, *token_str; + char c_dev_name[DISK_NAME_LEN]; + static const char colon[2] = ":"; + struct gendisk *disk; + char *tok; + + if (!ip_dev_maj_min) { + DMERR("invalid input device major:minor"); + r = -EINVAL; + goto out; + } + + l_dev_maj_min = kstrdup(ip_dev_maj_min, GFP_KERNEL); + + if (!l_dev_maj_min) { + r = -ENOMEM; + goto out; + } + + token_str = l_dev_maj_min; + + tok = strsep(&token_str, colon); + maj_min_count++; + + while (tok) { + len = strlen(tok); + + for (i = 0; i < len; i++) { + if (!isdigit(tok[i])) { + DMERR("device maj:min contains invalid char"); + r = -EINVAL; + goto out; + } + } + + if (maj_min_count == 1) + kstrtouint(tok, 10, &maj); + else if (maj_min_count == 2) + kstrtouint(tok, 10, &min); + else { + DMERR("device maj:min format is invalid"); + r = -EINVAL; + goto out; + } + + tok = strsep(&token_str, colon); + maj_min_count++; + } + + disk = (struct gendisk *)get_gendisk(MKDEV(maj, min), &part); + if (!disk) { + DMERR("device name with maj:min (%d:%d) not found", maj, min); + r = -ENODEV; + goto out; + } + + disk_name(disk, part, c_dev_name); + *op_dev_name = kstrdup(c_dev_name, GFP_KERNEL); + + if (!*op_dev_name) { + r = -ENOMEM; + goto out; + } + +out: + if (r < 0) + DMERR("get device name from maj:min failed %d", r); + + kfree(l_dev_maj_min); + return r; +} +EXPORT_SYMBOL(get_devname_from_maj_min); + +void dm_ima_init_measurements(struct target_type *tt) +{ + INIT_LIST_HEAD(&tt->ima_kv_list); + mutex_init(&tt->ima_lock); + tt->ima_last_buf_hash = NULL; + tt->ima_last_buf_hash_len = 0; +} +EXPORT_SYMBOL(dm_ima_init_measurements); + +void dm_ima_reset_measurement_list(struct target_type *tt) +{ + LIST_HEAD(temp_list); + + if (!tt) { + DMERR("invalid argument, target_type"); + return; + } + + mutex_lock(&tt->ima_lock); + list_cut_before(&temp_list, &tt->ima_kv_list, &tt->ima_kv_list); + mutex_unlock(&tt->ima_lock); + + dm_release_ima_measurements(&temp_list); +} +EXPORT_SYMBOL(dm_ima_reset_measurement_list); + +void dm_ima_append_measurement_list(struct target_type *tt, + const char *key, + const void *val, + unsigned int val_len) +{ + struct ima_keyval *cur_keyval = NULL; + int r = 0; + + if (!tt || !key || !val || val_len == 0) { + r = -EINVAL; + goto error; + } + + cur_keyval = kzalloc(sizeof(*cur_keyval), GFP_KERNEL); + if (!cur_keyval) { + r = -ENOMEM; + goto error; + } + + cur_keyval->key = kstrdup(key, GFP_KERNEL); + if (!cur_keyval->key) { + r = -ENOMEM; + goto error; + } + + cur_keyval->val_len = val_len; + cur_keyval->val = kmemdup(val, val_len, GFP_KERNEL); + if (!cur_keyval->val) { + r = -ENOMEM; + goto error; + } + + INIT_LIST_HEAD(&cur_keyval->kv_list); + + mutex_lock(&tt->ima_lock); + list_add(&cur_keyval->kv_list, &tt->ima_kv_list); + mutex_unlock(&tt->ima_lock); + + return; + +error: + if (cur_keyval) { + kzfree(cur_keyval->key); + kzfree(cur_keyval->val); + } + kfree(cur_keyval); + + DMERR("failed to append IMA measurement list %d", r); +} +EXPORT_SYMBOL(dm_ima_append_measurement_list); + +void dm_ima_finalize_and_measure(struct target_type *tt, + const char *buf_desc, + bool measure_buf_hash) +{ + char *evt_name = NULL, *evt_src = NULL, *equ = "=", *sep = ";"; + void *buf = NULL, *buf_hash = NULL, *last_buf_hash = NULL; + struct ima_keyval *cur_keyval = NULL, *tmp_keyval = NULL; + int cursor = 0, buf_len = 0, cur_keyname_len = 0; + int l_sep = strlen(sep), l_equ = strlen(equ); + int r = 0, hr = 0, mr = 0; + int buf_hash_len = 0; + struct timespec64 ts; + LIST_HEAD(temp_list); + + + if (!tt) { + r = -EINVAL; + goto out; + } + + mutex_lock(&tt->ima_lock); + if (!list_empty(&tt->ima_kv_list)) + list_cut_before(&temp_list, + &tt->ima_kv_list, + &tt->ima_kv_list); + else + r = -EINVAL; + + last_buf_hash = tt->ima_last_buf_hash; + tt->ima_last_buf_hash = NULL; + tt->ima_last_buf_hash_len = 0; + mutex_unlock(&tt->ima_lock); + + if (r) + goto out; + + list_for_each_entry_safe(cur_keyval, + tmp_keyval, + &temp_list, + kv_list) + buf_len += strlen(cur_keyval->key) + l_equ + + cur_keyval->val_len + l_sep; + + if (!buf_len) { + r = -EINVAL; + goto out; + } + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) { + r = -ENOMEM; + goto out; + } + + list_for_each_entry_safe(cur_keyval, + tmp_keyval, + &temp_list, + kv_list) { + cur_keyname_len = strlen(cur_keyval->key); + memcpy(buf+cursor, cur_keyval->key, cur_keyname_len); + cursor += cur_keyname_len; + memcpy(buf+cursor, equ, l_equ); + cursor += l_equ; + memcpy(buf+cursor, cur_keyval->val, cur_keyval->val_len); + cursor += cur_keyval->val_len; + memcpy(buf+cursor, sep, l_sep); + cursor += l_sep; + } + + hr = dm_compute_buffer_hash(buf, buf_len, &buf_hash, &buf_hash_len); + + if (!hr && buf_hash && last_buf_hash) + mr = memcmp(buf_hash, last_buf_hash, buf_hash_len); + + if (hr || mr || !last_buf_hash) { + ktime_get_real_ts64(&ts); + + evt_src = kasprintf(GFP_KERNEL, "dm-%s", tt->name); + if (!evt_src) { + r = -ENOMEM; + goto out; + } + + evt_name = kasprintf(GFP_KERNEL, "%lld:%09ld:%s%s%s", + ts.tv_sec, + ts.tv_nsec, + evt_src, + buf_desc ? ":" : "", + buf_desc ? buf_desc : ""); + + if (!evt_name) { + r = -ENOMEM; + goto out; + } + + ima_measure_critical_data((const char *)evt_name, + (const char *)evt_src, + (const void *)buf, + buf_len, + measure_buf_hash); + + kzfree(last_buf_hash); + + mutex_lock(&tt->ima_lock); + tt->ima_last_buf_hash = buf_hash; + tt->ima_last_buf_hash_len = buf_hash_len; + mutex_unlock(&tt->ima_lock); + + buf_hash = NULL; + } + + dm_release_ima_measurements(&temp_list); +out: + if (r) + DMERR("failed to measure DM target data through IMA %d", r); + + kfree(evt_src); + kzfree(evt_name); + kzfree(buf); + kzfree(buf_hash); +} +EXPORT_SYMBOL(dm_ima_finalize_and_measure); + +void dm_ima_exit_measurements(struct target_type *tt) +{ + dm_ima_reset_measurement_list(tt); + kzfree(tt->ima_last_buf_hash); + mutex_destroy(&tt->ima_lock); +} +EXPORT_SYMBOL(dm_ima_exit_measurements); + +MODULE_AUTHOR("Tushar Sugandhi "); +MODULE_DESCRIPTION("Enables IMA measurements for DM targets"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 93096e524e43..e396b0c7c9a6 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -164,6 +164,18 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode, struct dm_dev **result); void dm_put_device(struct dm_target *ti, struct dm_dev *d); +/* + * Information about IMA measurement data entry + */ +#ifdef CONFIG_IMA +struct ima_keyval { + char *key; + void *val; + unsigned int val_len; + struct list_head kv_list; +}; +#endif + /* * Information about a target type */ @@ -199,6 +211,13 @@ struct target_type { dm_dax_copy_iter_fn dax_copy_to_iter; dm_dax_zero_page_range_fn dax_zero_page_range; +#ifdef CONFIG_IMA + /* For ima measurements*/ + struct list_head ima_kv_list; + void *ima_last_buf_hash; + int ima_last_buf_hash_len; + struct mutex ima_lock; +#endif /* For internal device-mapper use. */ struct list_head list; }; @@ -533,6 +552,54 @@ struct dm_table *dm_swap_table(struct mapped_device *md, */ void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size); +/*----------------------------------------------------------------- + * Functions for ima measurements. + *----------------------------------------------------------------- + */ +#ifdef CONFIG_IMA +int get_devname_from_maj_min(const char *ip_dev_maj_min, char **op_dev_name); + +void dm_ima_init_measurements(struct target_type *tt); +/* + * Reset device mapper target's ima measurement list. + * If the measurements list need to be reset after dm_ima_init_measurements() + * and before calling dm_ima_finalize_and_measure(), this function should + * be called. This can be needed in scenarios like recovering from error + * paths and retrying measurements again. + */ +void dm_ima_reset_measurement_list(struct target_type *tt); + +void dm_ima_append_measurement_list(struct target_type *tt, + const char *key, + const void *val, + unsigned int val_length); + +void dm_ima_finalize_and_measure(struct target_type *tt, + const char *buf_desc, + bool measure_buf_hash); + +void dm_ima_exit_measurements(struct target_type *tt); +#else +static inline int get_devname_from_maj_min(const char *ip_dev_maj_min, + char **op_dev_name) +{ + return -EPERM; +} + +static inline void dm_ima_init_measurements(struct target_type *tt) {} + +static inline void dm_ima_reset_measurement_list(struct target_type *tt) {} + +static inline void dm_ima_append_measurement_list(struct target_type *tt, + const char *key, + const void *val, + unsigned int val_length) {} + +static inline void dm_ima_finalize_and_measure(struct target_type *tt, + const char *buf_desc, + bool measure_buf_hash) {} +static inline void dm_ima_exit_measurements(struct target_type *tt) {} +#endif /* CONFIG_IMA */ /*----------------------------------------------------------------- * Macros. *---------------------------------------------------------------*/ diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 4ab853461dff..f4932ed9ab84 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -302,6 +302,7 @@ static inline void add_disk_no_queue_reg(struct gendisk *disk) extern void del_gendisk(struct gendisk *gp); extern struct gendisk *get_gendisk(dev_t dev, int *partno); +extern char *disk_name(struct gendisk *hd, int partno, char *buf); extern struct block_device *bdget_disk(struct gendisk *disk, int partno); extern void set_device_ro(struct block_device *bdev, int flag); From patchwork Mon Oct 19 17:26: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: 11846017 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.0 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id EE726C43457 for ; Tue, 20 Oct 2020 08:22:51 +0000 (UTC) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 1DDA7222C8 for ; Tue, 20 Oct 2020 08:22:50 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 1DDA7222C8 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: mail.kernel.org; spf=tempfail smtp.mailfrom=dm-devel-bounces@redhat.com Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-367-7atytinrNsWiRnHNZtOHsQ-1; Tue, 20 Oct 2020 04:22:47 -0400 X-MC-Unique: 7atytinrNsWiRnHNZtOHsQ-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 2A1CF8049D5; Tue, 20 Oct 2020 08:22:42 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 0FA5A5B4B2; Tue, 20 Oct 2020 08:22:42 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id D3FDE8C7BA; Tue, 20 Oct 2020 08:22:41 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 09JHQXSJ012606 for ; Mon, 19 Oct 2020 13:26:33 -0400 Received: by smtp.corp.redhat.com (Postfix) id 3CF6E125B1F; Mon, 19 Oct 2020 17:26:33 +0000 (UTC) Received: from mimecast-mx02.redhat.com (mimecast02.extmail.prod.ext.rdu2.redhat.com [10.11.55.18]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 378A298911 for ; Mon, 19 Oct 2020 17:26:31 +0000 (UTC) Received: from us-smtp-1.mimecast.com (us-smtp-1.mimecast.com [207.211.31.81]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id E71C3803524 for ; Mon, 19 Oct 2020 17:26:30 +0000 (UTC) Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by relay.mimecast.com with ESMTP id us-mta-61-JB5Rd2cePr-df2wg11orHQ-1; Mon, 19 Oct 2020 13:26:25 -0400 X-MC-Unique: JB5Rd2cePr-df2wg11orHQ-1 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 4CEA820B36E7; Mon, 19 Oct 2020 10:26:24 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 4CEA820B36E7 From: Tushar Sugandhi To: zohar@linux.ibm.com, agk@redhat.com, snitzer@redhat.com, gmazyland@gmail.com Date: Mon, 19 Oct 2020 10:26:07 -0700 Message-Id: <20201019172607.16714-3-tusharsu@linux.microsoft.com> In-Reply-To: <20201019172607.16714-1-tusharsu@linux.microsoft.com> References: <20201019172607.16714-1-tusharsu@linux.microsoft.com> X-Mimecast-Impersonation-Protect: Policy=CLT - Impersonation Protection Definition; Similar Internal Domain=false; Similar Monitored External Domain=false; Custom External Domain=false; Mimecast External Domain=false; Newly Observed Domain=false; Internal User Name=false; Custom Display Name List=false; Reply-to Address Mismatch=false; Targeted Threat Dictionary=false; Mimecast Threat Dictionary=false; Custom Threat Dictionary=false X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-loop: dm-devel@redhat.com X-Mailman-Approved-At: Tue, 20 Oct 2020 04:22:11 -0400 Cc: sashal@kernel.org, jmorris@namei.org, linux-kernel@vger.kernel.org, nramas@linux.microsoft.com, dm-devel@redhat.com, tyhicks@linux.microsoft.com, linux-integrity@vger.kernel.org Subject: [dm-devel] [PATCH v4 2/2] dm-crypt: collect data and submit to DM to measure X-BeenThere: dm-devel@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: device-mapper development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: dm-devel-bounces@redhat.com Errors-To: dm-devel-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=dm-devel-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Currently, dm-crypt does not take advantage of IMA measuring capabilities, and ultimately the benefits of remote attestation. Measure various dm-crypt constructs by calling various device-mapper functions - dm_ima_*() that use IMA measuring capabilities. Implement ima_measure_dm_crypt_data() to measure various dm-crypt constructs. Ensure that ima_measure_dm_crypt_data() is non intrusive, i.e. failures in this function and the call-stack below should not affect the core functionality of dm-crypt. Register dm-crypt as supported data source for IMA measurement in ima.h. A demonstrative usage of above functionality on a system: If the IMA policy contains the following rule: measure func=CRITICAL_DATA data_sources=dm-crypt template=ima-buf and, the following commands are used to setup a crypt target: #key="faf453b4ee938cff2f0d2c869a0b743f59125c0a37f5bcd8f1dbbd911a78abaa" #arg="'0 1953125 crypt aes-xts-plain64 " #arg="$arg $key 0 " #arg="$arg /dev/loop0 0 1 allow_discards'" #tgt_name="test-crypt" #cmd="dmsetup create $tgt_name --table $arg" #eval $cmd then, the IMA log at /sys/kernel/security/integrity/ima/ascii_runtime_measurements should contain the dm-crypt measurements. And, the following IMA log entry should be added in the IMA log, ima-buf sha1:f418b90557619b42ade6b51476170e5f1a631a31 1603056402:556677963:dm-crypt:add_target 74695f6e756d5f646973636172645f62696f733d313b7065725f62696f5f646 174615f73697a653d3834383b646d7265715f73746172743d3136383b74666d 735f636f756e743d313b6f6e5f6469736b5f7461675f73697a653d303b696e7 46567726974795f69765f73697a653d303b696e746567726974795f7461675f 73697a653d303b69765f73697a653d31363b69765f6f66667365743d303b736 563746f725f73686966743d303b736563746f725f73697a653d3531323b666c 6167733d323b6369706865725f666c6167733d303b63635f73746172743d303 b6b65795f6d61635f73697a653d303b6b65795f65787472615f73697a653d30 3b6b65795f70617274733d313b6b65795f73697a653d33323b7461726765745 f6c656e6774683d313935333132353b7461726765745f626567696e3d303b63 69706865725f737472696e673d6165732d7874732d706c61696e36343b74617 26765745f6465766963655f6d616a6f725f6d696e6f723d3235333a313b7461 726765745f6465766963655f6e616d653d646d2d313b626173655f646576696 3655f6d616a6f725f6d696e6f723d373a303b626173655f6465766963655f6e 616d653d6c6f6f70303b where, the ascii representation of the above data is: ti_num_discard_bios=1;per_bio_data_size=848;dmreq_start=168; tfms_count=1;on_disk_tag_size=0;integrity_iv_size=0; integrity_tag_size=0;iv_size=16;iv_offset=0;sector_shift=0; sector_size=512;flags=2;cipher_flags=0;cc_start=0;key_mac_size=0; key_extra_size=0;key_parts=1;key_size=32;target_length=1953125; target_begin=0;cipher_string=aes-xts-plain64; target_device_major_minor=253:1;target_device_name=dm-1; base_device_major_minor=7:0;base_device_name=loop0; Some of the above values can be verified using: #dmsetup table --showkeys where, the output of the command should be similar to: test-crypt: 0 1953125 crypt aes-xts-plain64 faf453b4ee938cff2f0d2c869a0b743f59125c0a37f5bcd8f1dbbd911a78abaa 0 7:0 0 1 allow_discards Signed-off-by: Tushar Sugandhi --- drivers/md/dm-crypt.c | 217 ++++++++++++++++++++++++ security/integrity/ima/ima.h | 1 + security/integrity/ima/ima_queue_data.c | 3 +- 3 files changed, 220 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 148960721254..6d4030afa163 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -2529,6 +2529,8 @@ static void crypt_dtr(struct dm_target *ti) ti->private = NULL; + dm_ima_exit_measurements(ti->type); + if (!cc) return; @@ -2991,6 +2993,213 @@ static int crypt_report_zones(struct dm_target *ti, #endif +#ifdef CONFIG_IMA +/* + * append integer values to dm-crypt specific data + * to be measured through IMA + */ +static int ima_append_num_values(struct dm_target *ti, + const char *key, + long long num_val) +{ + char *num_str = NULL; + int length = 0; + int r = 0; + + if (!ti || !key) { + r = -EINVAL; + goto error; + } + + length = snprintf(NULL, 0, "%lld", num_val); + num_str = kzalloc(length + 1, GFP_KERNEL); + if (!num_str) { + r = -ENOMEM; + goto error; + } + snprintf(num_str, length + 1, "%lld", num_val); + dm_ima_append_measurement_list(ti->type, + key, + (const void *)num_str, + length); + +error: + if (r < 0) + DMERR("appending num values to IMA measurement list failed %d", r); + kzfree(num_str); + return r; +} +/* + * Measure dm-crypt specific data through IMA. + * It appends all the needed data to the list as a key-val pair using + * dm_ima_append_measurement_list() and internal ima_append_num_values(), + * and finally measures the list using dm_ima_finalize_and_measure(). + */ +static void ima_measure_dm_crypt_data(struct dm_target *ti, const char *desc) +{ + char *str_base_dev_name = NULL; + char *str_target_dev_name = NULL; + const char *str_target_dev_maj_min = NULL; + struct crypt_config *cc = NULL; + int r = 0; + + if (!ti) { + r = -EINVAL; + goto out; + } + + str_target_dev_maj_min = dm_table_device_name(ti->table); + if (!str_target_dev_maj_min) { + r = -EINVAL; + goto out; + } + + cc = ti->private; + + r = get_devname_from_maj_min(cc->dev->name, &str_base_dev_name); + if (r || !str_base_dev_name) { + r = -EINVAL; + goto out; + } + + dm_ima_append_measurement_list(ti->type, + "base_device_name", + (const void *)str_base_dev_name, + strlen(str_base_dev_name)); + + + dm_ima_append_measurement_list(ti->type, + "base_device_major_minor", + (const void *)cc->dev->name, + strlen(cc->dev->name)); + + r = get_devname_from_maj_min(str_target_dev_maj_min, + &str_target_dev_name); + if (r || !str_target_dev_name) { + r = -EINVAL; + goto out; + } + + dm_ima_append_measurement_list(ti->type, + "target_device_name", + (const void *)str_target_dev_name, + strlen(str_target_dev_name)); + + dm_ima_append_measurement_list(ti->type, + "target_device_major_minor", + (const void *)str_target_dev_maj_min, + strlen(str_target_dev_maj_min)); + + if (cc->cipher_string) { + dm_ima_append_measurement_list(ti->type, + "cipher_string", + (const void *)cc->cipher_string, + strlen(cc->cipher_string)); + } + + if (cc->cipher_auth) { + dm_ima_append_measurement_list(ti->type, + "cipher_auth", + (const void *)cc->cipher_auth, + strlen(cc->cipher_auth)); + } + + r = ima_append_num_values(ti, "target_begin", ti->begin); + if (r) + goto out; + + r = ima_append_num_values(ti, "target_length", ti->len); + if (r) + goto out; + + r = ima_append_num_values(ti, "key_size", cc->key_size); + if (r) + goto out; + + r = ima_append_num_values(ti, "key_parts", cc->key_parts); + if (r) + goto out; + + r = ima_append_num_values(ti, "key_extra_size", cc->key_extra_size); + if (r) + goto out; + + r = ima_append_num_values(ti, "key_mac_size", cc->key_mac_size); + if (r) + goto out; + + r = ima_append_num_values(ti, "cc_start", cc->start); + if (r) + goto out; + + r = ima_append_num_values(ti, "cipher_flags", cc->cipher_flags); + if (r) + goto out; + + r = ima_append_num_values(ti, "flags", cc->flags); + if (r) + goto out; + + r = ima_append_num_values(ti, "sector_size", cc->sector_size); + if (r) + goto out; + + r = ima_append_num_values(ti, "sector_shift", cc->sector_shift); + if (r) + goto out; + + r = ima_append_num_values(ti, "iv_offset", cc->iv_offset); + if (r) + goto out; + + r = ima_append_num_values(ti, "iv_size", cc->iv_size); + if (r) + goto out; + + r = ima_append_num_values(ti, "integrity_tag_size", cc->integrity_tag_size); + if (r) + goto out; + + r = ima_append_num_values(ti, "integrity_iv_size", cc->integrity_iv_size); + if (r) + goto out; + + r = ima_append_num_values(ti, "on_disk_tag_size", cc->on_disk_tag_size); + if (r) + goto out; + + r = ima_append_num_values(ti, "tfms_count", cc->tfms_count); + if (r) + goto out; + + r = ima_append_num_values(ti, "dmreq_start", cc->dmreq_start); + if (r) + goto out; + + r = ima_append_num_values(ti, "per_bio_data_size", cc->per_bio_data_size); + if (r) + goto out; + + r = ima_append_num_values(ti, "ti_num_discard_bios", + ti->num_discard_bios); + if (r) + goto out; + + dm_ima_finalize_and_measure(ti->type, desc, false); + +out: + if (r) + DMERR("IMA measurement of dm-crypt data failed %d", r); + + kzfree(str_base_dev_name); + kzfree(str_target_dev_name); +} +#else +static inline void ima_measure_dm_crypt_data(struct dm_target *ti, + const char *desc) {} +#endif /* CONFIG_IMA */ + + /* * Construct an encryption mapping: * [|:::] @@ -3186,6 +3395,10 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) ti->num_flush_bios = 1; + dm_ima_init_measurements(ti->type); + + ima_measure_dm_crypt_data(ti, "add_target"); + return 0; bad: @@ -3324,6 +3537,8 @@ static void crypt_postsuspend(struct dm_target *ti) struct crypt_config *cc = ti->private; set_bit(DM_CRYPT_SUSPENDED, &cc->flags); + + ima_measure_dm_crypt_data(ti, "post_suspend"); } static int crypt_preresume(struct dm_target *ti) @@ -3343,6 +3558,8 @@ static void crypt_resume(struct dm_target *ti) struct crypt_config *cc = ti->private; clear_bit(DM_CRYPT_SUSPENDED, &cc->flags); + + ima_measure_dm_crypt_data(ti, "resume"); } /* Message interface diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index e99e5e0db720..3d846c99c4ab 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -230,6 +230,7 @@ struct modsig; #define __ima_supported_kernel_data_sources(source) \ source(MIN_SOURCE, min_source) \ + source(DM_CRYPT, dm-crypt) \ source(MAX_SOURCE, max_source) #define __ima_enum_stringify(ENUM, str) (#str), diff --git a/security/integrity/ima/ima_queue_data.c b/security/integrity/ima/ima_queue_data.c index 9bf0b50024dd..c7ea0a644852 100644 --- a/security/integrity/ima/ima_queue_data.c +++ b/security/integrity/ima/ima_queue_data.c @@ -36,7 +36,8 @@ static bool timer_expired; static inline bool ima_queuing_enabled(void) { if ((IS_ENABLED(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) && - IS_ENABLED(CONFIG_SYSTEM_TRUSTED_KEYRING))) + IS_ENABLED(CONFIG_SYSTEM_TRUSTED_KEYRING)) || + IS_ENABLED(CONFIG_DM_CRYPT)) return true; return false;