From patchwork Mon Jul 26 16:36:50 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 486082 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=-21.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, MENTIONS_GIT_HOSTING, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT 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 AEBF0C43214 for ; Mon, 26 Jul 2021 16:40:22 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9957160F55 for ; Mon, 26 Jul 2021 16:40:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239328AbhGZP7w (ORCPT ); Mon, 26 Jul 2021 11:59:52 -0400 Received: from frasgout.his.huawei.com ([185.176.79.56]:3486 "EHLO frasgout.his.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239750AbhGZP5z (ORCPT ); Mon, 26 Jul 2021 11:57:55 -0400 Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.206]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4GYQFv5XXVz6GCrW; Tue, 27 Jul 2021 00:23:15 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2176.2; Mon, 26 Jul 2021 18:38:15 +0200 From: Roberto Sassu To: , , CC: , , , , , Roberto Sassu Subject: [RFC][PATCH v2 02/12] diglim: Basic definitions Date: Mon, 26 Jul 2021 18:36:50 +0200 Message-ID: <20210726163700.2092768-3-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210726163700.2092768-1-roberto.sassu@huawei.com> References: <20210726163700.2092768-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml754-chm.china.huawei.com (10.201.108.204) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Introduce the basic definitions, exported to user space, to use digest lists. The definitions, added to include/uapi/linux/diglim.h, are documented in Documentation/security/diglim/implementation.rst. Signed-off-by: Roberto Sassu --- .../security/diglim/implementation.rst | 97 +++++++++++++++++++ Documentation/security/diglim/index.rst | 1 + MAINTAINERS | 2 + include/uapi/linux/diglim.h | 51 ++++++++++ 4 files changed, 151 insertions(+) create mode 100644 Documentation/security/diglim/implementation.rst create mode 100644 include/uapi/linux/diglim.h diff --git a/Documentation/security/diglim/implementation.rst b/Documentation/security/diglim/implementation.rst new file mode 100644 index 000000000000..59a180b3bb3f --- /dev/null +++ b/Documentation/security/diglim/implementation.rst @@ -0,0 +1,97 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Implementation +============== + +This section describes the implementation of DIGLIM. + + +Basic Definitions +----------------- + +This section introduces the basic definitions required to use DIGLIM. + + +Compact Digest List Format +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: include/uapi/linux/diglim.h + :identifiers: compact_list_hdr + +Compact Types +............. + +Digests can be of different types: + +- ``COMPACT_PARSER``: digests of executables which are given the ability to + parse digest lists not in the compact format and to upload to the kernel + the digest list converted to the compact format; +- ``COMPACT_FILE``: digests of regular files; +- ``COMPACT_METADATA``: digests of file metadata (e.g. the digest + calculated by EVM to verify a portable signature); +- ``COMPACT_DIGEST_LIST``: digests of digest lists (only used internally by + the kernel). + +Different users of DIGLIM might query digests with different compact types. +For example, IMA would be interested in COMPACT_FILE, as it deals with +regular files, while EVM would be interested in COMPACT_METADATA, as it +verifies file metadata. + + +Compact Modifiers +................. + +Digests can also have specific attributes called modifiers (bit position): + +- ``COMPACT_MOD_IMMUTABLE``: file content or metadata should not be + modifiable. + +IMA might use this information to deny open for writing, or EVM to deny +setxattr operations. + + +Actions +....... + +This section defines a set of possible actions that have been executed on +the digest lists (bit position): + +- ``COMPACT_ACTION_IMA_MEASURED``: the digest list has been measured by + IMA; +- ``COMPACT_ACTION_IMA_APPRAISED``: the digest list has been successfully + appraised by IMA; +- ``COMPACT_ACTION_IMA_APPRAISED_DIGSIG``: the digest list has been + successfully appraised by IMA by verifying a digital signature. + +This information might help users of DIGLIM to decide whether to use the +result of a queried digest. + +For example, if a digest belongs to a digest list that was not measured +before, IMA should ignore the result of the query, as the measurement list +sent to remote verifiers would lack which digests have been uploaded to the +kernel. + + +Compact Digest List Example +........................... + +:: + + version: 1, type: 2, modifiers: 0 algo: 4, count: 3, datalen: 96 + + version: 1, type: 3, modifiers: 1 algo: 6, count: 2, datalen: 128 + + +This digest list consists of two blocks. The first block contains three +SHA256 digests of regular files. The second block contains two SHA512 +digests of immutable metadata. + + +Compact Digest List Operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Finally, this section defines the possible operations that can be performed +with digest lists: + +- ``DIGEST_LIST_ADD``: the digest list is being added; +- ``DIGEST_LIST_DEL``: the digest list is being deleted. diff --git a/Documentation/security/diglim/index.rst b/Documentation/security/diglim/index.rst index 0fc5ab019bc0..4771134c2f0d 100644 --- a/Documentation/security/diglim/index.rst +++ b/Documentation/security/diglim/index.rst @@ -9,3 +9,4 @@ Digest Lists Integrity Module (DIGLIM) introduction architecture + implementation diff --git a/MAINTAINERS b/MAINTAINERS index c914dadd7e65..f61f5239468a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5458,8 +5458,10 @@ L: linux-integrity@vger.kernel.org S: Supported T: git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git F: Documentation/security/diglim/architecture.rst +F: Documentation/security/diglim/implementation.rst F: Documentation/security/diglim/index.rst F: Documentation/security/diglim/introduction.rst +F: include/uapi/linux/diglim.h DIOLAN U2C-12 I2C DRIVER M: Guenter Roeck diff --git a/include/uapi/linux/diglim.h b/include/uapi/linux/diglim.h new file mode 100644 index 000000000000..8a33d1f0fefb --- /dev/null +++ b/include/uapi/linux/diglim.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH + * + * Author: Roberto Sassu + * + * DIGLIM definitions exported to user space, useful for generating digest + * lists. + */ + +#ifndef _UAPI__LINUX_DIGLIM_H +#define _UAPI__LINUX_DIGLIM_H + +#include +#include + +enum compact_types { COMPACT_KEY, COMPACT_PARSER, COMPACT_FILE, + COMPACT_METADATA, COMPACT_DIGEST_LIST, COMPACT__LAST }; + +enum compact_modifiers { COMPACT_MOD_IMMUTABLE, COMPACT_MOD__LAST }; + +enum compact_actions { COMPACT_ACTION_IMA_MEASURED, + COMPACT_ACTION_IMA_APPRAISED, + COMPACT_ACTION_IMA_APPRAISED_DIGSIG, + COMPACT_ACTION__LAST }; + +enum ops { DIGEST_LIST_ADD, DIGEST_LIST_DEL, DIGEST_LIST_OP__LAST }; + +/** + * struct compact_list_hdr - header of the following concatenated digests + * @version: version of the digest list + * @_reserved: field reserved for future use + * @type: type of digest list among enum compact_types + * @modifiers: additional attributes among (1 << enum compact_modifiers) + * @algo: digest algorithm + * @count: number of digests + * @datalen: length of concatenated digests + * + * A digest list is a set of blocks composed by struct compact_list_hdr and + * the following concatenated digests. + */ +struct compact_list_hdr { + __u8 version; + __u8 _reserved; + __le16 type; + __le16 modifiers; + __le16 algo; + __le32 count; + __le32 datalen; +} __packed; +#endif /*_UAPI__LINUX_DIGLIM_H*/ From patchwork Mon Jul 26 16:36:51 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 486080 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=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT 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 79C7FC19F37 for ; Mon, 26 Jul 2021 16:40:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5D6AB60F6B for ; Mon, 26 Jul 2021 16:40:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239567AbhGZP7y (ORCPT ); Mon, 26 Jul 2021 11:59:54 -0400 Received: from frasgout.his.huawei.com ([185.176.79.56]:3487 "EHLO frasgout.his.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239549AbhGZP5z (ORCPT ); Mon, 26 Jul 2021 11:57:55 -0400 Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.206]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4GYQKf29Rpz6L9PW; Tue, 27 Jul 2021 00:26:30 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2176.2; Mon, 26 Jul 2021 18:38:16 +0200 From: Roberto Sassu To: , , CC: , , , , , Roberto Sassu Subject: [RFC][PATCH v2 03/12] diglim: Objects Date: Mon, 26 Jul 2021 18:36:51 +0200 Message-ID: <20210726163700.2092768-4-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210726163700.2092768-1-roberto.sassu@huawei.com> References: <20210726163700.2092768-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml754-chm.china.huawei.com (10.201.108.204) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Define the objects to manage digest lists: - digest_list_item: a digest list loaded into the kernel; - digest_list_item_ref: a reference to a digest list; - digest_item: a digest of a digest list. Also define some helpers for the objects. More information can be found in Documentation/security/diglim/implementation.rst. Signed-off-by: Roberto Sassu --- .../security/diglim/implementation.rst | 105 ++++++++++++++ MAINTAINERS | 1 + security/integrity/diglim/diglim.h | 134 ++++++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 security/integrity/diglim/diglim.h diff --git a/Documentation/security/diglim/implementation.rst b/Documentation/security/diglim/implementation.rst index 59a180b3bb3f..6002049612a1 100644 --- a/Documentation/security/diglim/implementation.rst +++ b/Documentation/security/diglim/implementation.rst @@ -95,3 +95,108 @@ with digest lists: - ``DIGEST_LIST_ADD``: the digest list is being added; - ``DIGEST_LIST_DEL``: the digest list is being deleted. + + +Objects +------- + +This section defines the objects to manage digest lists. + +.. kernel-doc:: security/integrity/diglim/diglim.h + +They are represented in the following class diagram: + +:: + + digest_offset, + hdr_offset---------------+ + | + | + +------------------+ | +----------------------+ + | digest_list_item |--- N:1 ---| digest_list_item_ref | + +------------------+ +----------------------+ + | + 1:N + | + +-------------+ + | digest_item | + +-------------+ + +A ``digest_list_item`` is associated to one or multiple +``digest_list_item_ref``, one for each digest it contains. However, +a ``digest_list_item_ref`` is associated to only one ``digest_list_item``, +as it represents a single location within a specific digest list. + +Given that a ``digest_list_item_ref`` represents a single location, it is +associated to only one ``digest_item``. However, a ``digest_item`` can have +multiple references (as it might appears multiple times within the same +digest list or in different digest lists, if it is duplicated). + +All digest list references are stored for a given digest, so that a query +result can include the OR of the modifiers and actions of each referenced +digest list. + +The relationship between the described objects can be graphically +represented as: + +:: + + Hash table +-------------+ +-------------+ + PARSER +-----+ | digest_item | | digest_item | + FILE | key |-->| |-->...-->| | + METADATA +-----+ |ref0|...|refN| |ref0|...|refN| + +-------------+ +-------------+ + ref0: | | refN: + digest_offset | +-----------------------------+ digest_offset + hdr_offset | | hdr_offset + | | + V V + +--------------------+ + | digest_list_item | + | | + | size, buf, actions | + +--------------------+ + ^ + | + Hash table +-------------+ +-------------+ + DIGEST_LIST +-----+ |ref0 | |ref0 | + | key |-->| |-->...-->| | + +-----+ | digest_item | | digest_item | + +-------------+ +-------------+ + +The reference for the digest of the digest list differs from the references +for the other digest types. ``digest_offset`` and ``hdr_offset`` are set to +zero, so that the digest of the digest list is retrieved from the +``digest_list_item`` structure directly (see ``get_digest()`` below). + +Finally, this section defines useful helpers to access a digest or the +header the digest belongs to. For example: + +:: + + static inline struct compact_list_hdr *get_hdr( + struct digest_list_item *digest_list, + loff_t hdr_offset) + { + return (struct compact_list_hdr *)(digest_list->buf + hdr_offset); + } + +the header can be obtained by summing the address of the digest list buffer +in the ``digest_list_item`` structure with ``hdr_offset``. + +Similarly: + +:: + + static inline u8 *get_digest(struct digest_list_item *digest_list, + loff_t digest_offset, loff_t hdr_offset) + { + /* Digest list digest is stored in a different place. */ + if (!digest_offset) + return digest_list->digest; + return digest_list->buf + digest_offset; + } + +the digest can be obtained by summing the address of the digest list buffer +with ``digest_offset`` (except for the digest lists, where the digest is +stored in the ``digest`` field of the ``digest_list_item`` structure). diff --git a/MAINTAINERS b/MAINTAINERS index f61f5239468a..f7592d41367d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5462,6 +5462,7 @@ F: Documentation/security/diglim/implementation.rst F: Documentation/security/diglim/index.rst F: Documentation/security/diglim/introduction.rst F: include/uapi/linux/diglim.h +F: security/integrity/diglim/diglim.h DIOLAN U2C-12 I2C DRIVER M: Guenter Roeck diff --git a/security/integrity/diglim/diglim.h b/security/integrity/diglim/diglim.h new file mode 100644 index 000000000000..578253d7e1d1 --- /dev/null +++ b/security/integrity/diglim/diglim.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005,2006,2007,2008 IBM Corporation + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH + * + * Author: Roberto Sassu + * + * Definitions only used inside DIGLIM. + */ + +#ifndef __DIGLIM_INTERNAL_H +#define __DIGLIM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_DIGEST_SIZE 64 +#define HASH_BITS 10 +#define DIGLIM_HTABLE_SIZE (1 << HASH_BITS) + +/** + * struct digest_list_item - a digest list loaded into the kernel + * + * @size: size of the digest list buffer + * @buf: digest list buffer + * @digest: digest of the digest list + * @label: label used to identify the digest list (e.g. file name) + * @actions: actions performed on the digest list + * @algo: digest algorithm + */ +struct digest_list_item { + loff_t size; + u8 *buf; + u8 digest[64]; + const char *label; + u8 actions; + enum hash_algo algo; +}; + +/** + * struct digest_list_item_ref - a reference to a digest list + * + * @list: linked list pointers + * @digest_list: pointer to struct digest_list_item + * @digest_offset: offset of the digest in the referenced digest list + * @hdr_offset: offset of the header the digest refers to in the digest list + */ +struct digest_list_item_ref { + struct list_head list; + struct digest_list_item *digest_list; + u32 digest_offset; + u32 hdr_offset; +}; + +/** + * struct digest_item - a digest of a digest list + * + * @hnext: pointers of the hash table + * @refs: linked list of struct digest_list_item_ref + */ +struct digest_item { + struct hlist_node hnext; + struct list_head refs; +}; + +struct h_table { + unsigned long len; + struct hlist_head queue[DIGLIM_HTABLE_SIZE]; +}; + +static inline unsigned int hash_key(u8 *digest) +{ + return (digest[0] | digest[1] << 8) % DIGLIM_HTABLE_SIZE; +} + +static inline struct compact_list_hdr *get_hdr( + struct digest_list_item *digest_list, + loff_t hdr_offset) +{ + return (struct compact_list_hdr *)(digest_list->buf + hdr_offset); +} + +static inline enum hash_algo get_algo(struct digest_list_item *digest_list, + loff_t digest_offset, loff_t hdr_offset) +{ + /* Digest list digest algorithm is stored in a different place. */ + if (!digest_offset) + return digest_list->algo; + + return get_hdr(digest_list, hdr_offset)->algo; +} + +static inline u8 *get_digest(struct digest_list_item *digest_list, + loff_t digest_offset, loff_t hdr_offset) +{ + /* Digest list digest is stored in a different place. */ + if (!digest_offset) + return digest_list->digest; + + return digest_list->buf + digest_offset; +} + +static inline struct compact_list_hdr *get_hdr_ref( + struct digest_list_item_ref *ref) +{ + return get_hdr(ref->digest_list, ref->hdr_offset); +} + +static inline enum hash_algo get_algo_ref(struct digest_list_item_ref *ref) +{ + /* Digest list digest algorithm is stored in a different place. */ + if (!ref->digest_offset) + return ref->digest_list->algo; + + return get_hdr_ref(ref)->algo; +} + +static inline u8 *get_digest_ref(struct digest_list_item_ref *ref) +{ + /* Digest list digest is stored in a different place. */ + if (!ref->digest_offset) + return ref->digest_list->digest; + + return ref->digest_list->buf + ref->digest_offset; +} +#endif /*__DIGLIM_INTERNAL_H*/ From patchwork Mon Jul 26 16:36:54 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 486077 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=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT 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 3F658C4320E for ; Mon, 26 Jul 2021 16:41:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 22B2460F5E for ; Mon, 26 Jul 2021 16:41:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232726AbhGZQBK (ORCPT ); Mon, 26 Jul 2021 12:01:10 -0400 Received: from frasgout.his.huawei.com ([185.176.79.56]:3490 "EHLO frasgout.his.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239967AbhGZP7F (ORCPT ); Mon, 26 Jul 2021 11:59:05 -0400 Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.207]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4GYQQ80yMKz6G9Ny; Tue, 27 Jul 2021 00:30:24 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2176.2; Mon, 26 Jul 2021 18:39:31 +0200 From: Roberto Sassu To: , , CC: , , , , , Roberto Sassu Subject: [RFC][PATCH v2 06/12] diglim: Interfaces - digest_list_add, digest_list_del Date: Mon, 26 Jul 2021 18:36:54 +0200 Message-ID: <20210726163700.2092768-7-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210726163700.2092768-1-roberto.sassu@huawei.com> References: <20210726163700.2092768-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml754-chm.china.huawei.com (10.201.108.204) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Introduce /integrity/diglim/digest_list_add, which can be used to upload a digest list and add the digests to the hash table; passed data are interpreted as file path if the first byte is / or as the digest list itself otherwise. ima_measure_critical_data() is called to calculate the digest of the digest list and eventually, if an appropriate rule is set in the IMA policy, to measure it. Also introduce /integrity/diglim/digest_list_del, which can be used to upload a digest list and delete the digests from the hash table; data are interpreted in the same way as described for digest_list_add. Signed-off-by: Roberto Sassu --- .../security/diglim/implementation.rst | 9 + MAINTAINERS | 1 + include/linux/kernel_read_file.h | 1 + security/integrity/diglim/Makefile | 2 +- security/integrity/diglim/diglim.h | 2 + security/integrity/diglim/fs.c | 239 ++++++++++++++++++ security/integrity/integrity.h | 4 + 7 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 security/integrity/diglim/fs.c diff --git a/Documentation/security/diglim/implementation.rst b/Documentation/security/diglim/implementation.rst index 9d679567a037..fc0cd8810a80 100644 --- a/Documentation/security/diglim/implementation.rst +++ b/Documentation/security/diglim/implementation.rst @@ -244,3 +244,12 @@ back when one of the steps fails. #. digest_list_parse() deletes the struct digest_list_item on unsuccessful add or successful delete. + + +Interfaces +---------- + +This section introduces the interfaces in +``/integrity/diglim`` necessary to interact with DIGLIM. + +.. kernel-doc:: security/integrity/diglim/fs.c diff --git a/MAINTAINERS b/MAINTAINERS index 77c3613c600a..0672128fae7f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5464,6 +5464,7 @@ F: Documentation/security/diglim/introduction.rst F: include/linux/diglim.h F: include/uapi/linux/diglim.h F: security/integrity/diglim/diglim.h +F: security/integrity/diglim/fs.c F: security/integrity/diglim/methods.c F: security/integrity/diglim/parser.c diff --git a/include/linux/kernel_read_file.h b/include/linux/kernel_read_file.h index 575ffa1031d3..636ecdfdc616 100644 --- a/include/linux/kernel_read_file.h +++ b/include/linux/kernel_read_file.h @@ -14,6 +14,7 @@ id(KEXEC_INITRAMFS, kexec-initramfs) \ id(POLICY, security-policy) \ id(X509_CERTIFICATE, x509-certificate) \ + id(DIGEST_LIST, digest-list) \ id(MAX_ID, ) #define __fid_enumify(ENUM, dummy) READING_ ## ENUM, diff --git a/security/integrity/diglim/Makefile b/security/integrity/diglim/Makefile index 34e4e154fff3..ac345afdf5dd 100644 --- a/security/integrity/diglim/Makefile +++ b/security/integrity/diglim/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_DIGLIM) += diglim.o -diglim-y := methods.o parser.o +diglim-y := methods.o parser.o fs.o diff --git a/security/integrity/diglim/diglim.h b/security/integrity/diglim/diglim.h index 3adc218a0325..819703175eda 100644 --- a/security/integrity/diglim/diglim.h +++ b/security/integrity/diglim/diglim.h @@ -22,6 +22,8 @@ #include #include +#include "../integrity.h" + #define MAX_DIGEST_SIZE 64 #define HASH_BITS 10 #define DIGLIM_HTABLE_SIZE (1 << HASH_BITS) diff --git a/security/integrity/diglim/fs.c b/security/integrity/diglim/fs.c new file mode 100644 index 000000000000..07a0d75a0e33 --- /dev/null +++ b/security/integrity/diglim/fs.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2005,2006,2007,2008 IBM Corporation + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH + * + * Author: Roberto Sassu + * + * Functions for the interfaces exposed in securityfs. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diglim.h" + +#define MAX_DIGEST_LIST_SIZE (64 * 1024 * 1024 - 1) + +static struct dentry *diglim_dir; +/** + * DOC: digest_list_add + * + * digest_list_add can be used to upload a digest list and add the digests + * to the hash table; passed data are interpreted as file path if the first + * byte is ``/`` or as the digest list itself otherwise. + * + * ima_measure_critical_data() is called to calculate the digest of the + * digest list and eventually, if an appropriate rule is set in the IMA + * policy, to measure it. + */ +static struct dentry *digest_list_add_dentry; +/** + * DOC: digest_list_del + * + * digest_list_del can be used to upload a digest list and delete the + * digests from the hash table; data are interpreted in the same way as + * described for digest_list_add. + */ +static struct dentry *digest_list_del_dentry; +char digest_label[NAME_MAX + 1]; + +/* + * digest_list_read: read and parse the digest list from the path + */ +static ssize_t digest_list_read(char *path, enum ops op) +{ + void *data = NULL; + char *datap; + size_t size; + u8 actions = 0; + struct file *file; + char event_name[NAME_MAX + 9 + 1]; + u8 digest[IMA_MAX_DIGEST_SIZE] = { 0 }; + enum hash_algo algo; + int rc, pathlen = strlen(path); + + /* Remove \n. */ + datap = path; + strsep(&datap, "\n"); + + file = filp_open(path, O_RDONLY, 0); + if (IS_ERR(file)) { + pr_err("unable to open file: %s (%ld)", path, PTR_ERR(file)); + return PTR_ERR(file); + } + + rc = kernel_read_file(file, 0, &data, INT_MAX, NULL, + READING_DIGEST_LIST); + if (rc < 0) { + pr_err("unable to read file: %s (%d)", path, rc); + goto out; + } + + size = rc; + + snprintf(event_name, sizeof(event_name), "%s_file_%s", + op == DIGEST_LIST_ADD ? "add" : "del", + file_dentry(file)->d_name.name); + + rc = ima_measure_critical_data("diglim", event_name, data, size, false, + digest, sizeof(digest)); + if (rc < 0 && rc != -EEXIST) + goto out_vfree; + + algo = ima_get_current_hash_algo(); + + if (!rc || rc == -EEXIST) + actions |= (1 << COMPACT_ACTION_IMA_MEASURED); + + rc = digest_list_parse(size, data, op, actions, digest, algo, ""); + if (rc < 0) + pr_err("unable to upload digest list %s (%d)\n", path, rc); +out_vfree: + vfree(data); +out: + fput(file); + + if (rc < 0) + return rc; + + return pathlen; +} + +/* + * digest_list_write: write the digest list path or the digest list itself + */ +static ssize_t digest_list_write(struct file *file, const char __user *buf, + size_t datalen, loff_t *ppos) +{ + char *data; + ssize_t result; + enum ops op = DIGEST_LIST_ADD; + struct dentry *dentry = file_dentry(file); + u8 digest[IMA_MAX_DIGEST_SIZE]; + char event_name[NAME_MAX + 11 + 1]; + enum hash_algo algo; + u8 actions = 0; + + /* No partial writes. */ + result = -EINVAL; + if (*ppos != 0) + goto out; + + result = -EFBIG; + if (datalen > MAX_DIGEST_LIST_SIZE) + goto out; + + data = memdup_user_nul(buf, datalen); + if (IS_ERR(data)) { + result = PTR_ERR(data); + goto out; + } + + if (dentry == digest_list_del_dentry) + op = DIGEST_LIST_DEL; + + result = -EPERM; + + if (data[0] == '/') { + result = digest_list_read(data, op); + } else { + snprintf(event_name, sizeof(event_name), "%s_buffer_%s", + op == DIGEST_LIST_ADD ? "add" : "del", digest_label); + + result = ima_measure_critical_data("diglim", event_name, data, + datalen, false, digest, + sizeof(digest)); + if (result < 0 && result != -EEXIST) { + pr_err("failed to measure buffer digest (%ld)\n", + result); + goto out_kfree; + } + + algo = ima_get_current_hash_algo(); + + if (!result || result == -EEXIST) + actions |= (1 << COMPACT_ACTION_IMA_MEASURED); + + result = digest_list_parse(datalen, data, op, actions, digest, + algo, ""); + if (result != datalen) { + pr_err("unable to upload generated digest list\n"); + result = -EINVAL; + } + } +out_kfree: + kfree(data); +out: + return result; +} + +static unsigned long flags; + +/* + * digest_list_open: sequentialize access to the add/del files + */ +static int digest_list_open(struct inode *inode, struct file *filp) +{ + if ((filp->f_flags & O_ACCMODE) != O_WRONLY) + return -EACCES; + + if (test_and_set_bit(0, &flags)) + return -EBUSY; + + return 0; +} + +/* + * digest_list_release - release the add/del files + */ +static int digest_list_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &flags); + return 0; +} + +static const struct file_operations digest_list_upload_ops = { + .open = digest_list_open, + .write = digest_list_write, + .read = seq_read, + .release = digest_list_release, + .llseek = generic_file_llseek, +}; + +static int __init diglim_fs_init(void) +{ + diglim_dir = securityfs_create_dir("diglim", integrity_dir); + if (IS_ERR(diglim_dir)) + return -1; + + digest_list_add_dentry = securityfs_create_file("digest_list_add", 0200, + diglim_dir, NULL, + &digest_list_upload_ops); + if (IS_ERR(digest_list_add_dentry)) + goto out; + + digest_list_del_dentry = securityfs_create_file("digest_list_del", 0200, + diglim_dir, NULL, + &digest_list_upload_ops); + if (IS_ERR(digest_list_del_dentry)) + goto out; + + return 0; +out: + securityfs_remove(digest_list_del_dentry); + securityfs_remove(digest_list_add_dentry); + securityfs_remove(diglim_dir); + return -1; +} + +late_initcall(diglim_fs_init); diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 547425c20e11..ac45e1599c2d 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -6,6 +6,9 @@ * Mimi Zohar */ +#ifndef __INTEGRITY_H +#define __INTEGRITY_H + #ifdef pr_fmt #undef pr_fmt #endif @@ -283,3 +286,4 @@ static inline void __init add_to_platform_keyring(const char *source, { } #endif +#endif /*__INTEGRITY_H*/ From patchwork Mon Jul 26 16:36:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 486081 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=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT 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 C5F7CC19F3D for ; Mon, 26 Jul 2021 16:40:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C078C60F11 for ; Mon, 26 Jul 2021 16:40:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237400AbhGZP75 (ORCPT ); Mon, 26 Jul 2021 11:59:57 -0400 Received: from frasgout.his.huawei.com ([185.176.79.56]:3493 "EHLO frasgout.his.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239997AbhGZP7H (ORCPT ); Mon, 26 Jul 2021 11:59:07 -0400 Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.226]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4GYQM72htqz6L9lV; Tue, 27 Jul 2021 00:27:47 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2176.2; Mon, 26 Jul 2021 18:39:33 +0200 From: Roberto Sassu To: , , CC: , , , , , Roberto Sassu Subject: [RFC][PATCH v2 09/12] diglim: Interfaces - digest_query Date: Mon, 26 Jul 2021 18:36:57 +0200 Message-ID: <20210726163700.2092768-10-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210726163700.2092768-1-roberto.sassu@huawei.com> References: <20210726163700.2092768-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml754-chm.china.huawei.com (10.201.108.204) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Introduce the digest_query interface, which allows to write a query in the format - and to obtain all digest lists that include that digest. Signed-off-by: Roberto Sassu --- security/integrity/diglim/fs.c | 154 +++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/security/integrity/diglim/fs.c b/security/integrity/diglim/fs.c index 8958e987f708..f1c1fc56448a 100644 --- a/security/integrity/diglim/fs.c +++ b/security/integrity/diglim/fs.c @@ -43,6 +43,13 @@ static struct dentry *digest_lists_loaded_dir; * list (buffer) loaded through digest_list_add. */ static struct dentry *digest_label_dentry; +/** + * DOC: digest_query + * + * digest_query allows to write a query in the format - and + * to obtain all digest lists that include that digest. + */ +static struct dentry *digest_query_dentry; /** * DOC: digest_list_add * @@ -63,6 +70,7 @@ static struct dentry *digest_list_add_dentry; * described for digest_list_add. */ static struct dentry *digest_list_del_dentry; +char digest_query[CRYPTO_MAX_ALG_NAME + 1 + IMA_MAX_DIGEST_SIZE * 2 + 1]; char digest_label[NAME_MAX + 1]; static int parse_digest_list_filename(const char *digest_list_filename, @@ -253,6 +261,84 @@ static const struct file_operations digest_list_ascii_ops = { .release = seq_release, }; +/* + * *pos is the n-th reference to show among all the references in all digest + * items found with the query. + */ +static void *digest_query_start(struct seq_file *m, loff_t *pos) +{ + struct digest_item *d; + u8 digest[IMA_MAX_DIGEST_SIZE]; + enum hash_algo algo; + loff_t count = 0; + enum compact_types type = 0; + struct digest_list_item_ref *ref; + int ret; + + ret = parse_digest_list_filename(digest_query, digest, &algo); + if (ret < 0) + return NULL; + + for (type = 0; type < COMPACT__LAST; type++) { + d = __digest_lookup(digest, algo, type, NULL, NULL); + if (!d) + continue; + + list_for_each_entry(ref, &d->refs, list) { + if (count++ == *pos) { + m->private = d; + return ref; + } + } + } + + return NULL; +} + +static void *digest_query_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct digest_item *d = (struct digest_item *)m->private; + struct digest_list_item_ref *cur_ref = (struct digest_list_item_ref *)v; + struct digest_list_item_ref *ref; + + (*pos)++; + + list_for_each_entry(ref, &d->refs, list) { + if (ref != cur_ref) + continue; + + if (!list_is_last(&cur_ref->list, &d->refs)) + return list_next_entry(cur_ref, list); + } + + return NULL; +} + +static void digest_query_stop(struct seq_file *m, void *v) +{ +} + +static int digest_query_show(struct seq_file *m, void *v) +{ + struct digest_list_item_ref *ref = (struct digest_list_item_ref *)v; + struct digest_list_item *digest_list = ref->digest_list; + struct compact_list_hdr *hdr = get_hdr_ref(ref); + + if (!ref->digest_offset) { + seq_printf(m, "%s (actions: %d): type: %d, size: %lld\n", + digest_list->label, digest_list->actions, + COMPACT_DIGEST_LIST, digest_list->size); + return 0; + } + + seq_printf(m, + "%s (actions: %d): version: %d, algo: %s, type: %d, modifiers: %d, count: %d, datalen: %d\n", + digest_list->label, digest_list->actions, hdr->version, + hash_algo_name[hdr->algo], hdr->type, hdr->modifiers, + hdr->count, hdr->datalen); + return 0; +} + static int digest_list_get_secfs_files(char *label, u8 *digest, enum hash_algo algo, enum ops op, struct dentry **dentry, @@ -538,6 +624,67 @@ static const struct file_operations digest_label_ops = { .llseek = generic_file_llseek, }; +static const struct seq_operations digest_query_seqops = { + .start = digest_query_start, + .next = digest_query_next, + .stop = digest_query_stop, + .show = digest_query_show, +}; + +/* + * digest_query_open: sequentialize access to the add/del/query files + */ +static int digest_query_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &flags)) + return -EBUSY; + + if (file->f_flags & O_WRONLY) + return 0; + + return seq_open(file, &digest_query_seqops); +} + +/* + * digest_query_write: write digest query (-). + */ +static ssize_t digest_query_write(struct file *file, const char __user *buf, + size_t datalen, loff_t *ppos) +{ + int rc; + + if (datalen >= sizeof(digest_query)) + return -EINVAL; + + rc = copy_from_user(digest_query, buf, datalen); + if (rc) + return -EFAULT; + + digest_query[datalen] = '\0'; + return datalen; +} + +/* + * digest_query_release - release the query file + */ +static int digest_query_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &flags); + + if (file->f_flags & O_WRONLY) + return 0; + + return seq_release(inode, file); +} + +static const struct file_operations digest_query_ops = { + .open = digest_query_open, + .write = digest_query_write, + .read = seq_read, + .release = digest_query_release, + .llseek = generic_file_llseek, +}; + static int __init diglim_fs_init(void) { diglim_dir = securityfs_create_dir("diglim", integrity_dir); @@ -567,8 +714,15 @@ static int __init diglim_fs_init(void) if (IS_ERR(digest_label_dentry)) goto out; + digest_query_dentry = securityfs_create_file("digest_query", 0600, + diglim_dir, NULL, + &digest_query_ops); + if (IS_ERR(digest_query_dentry)) + goto out; + return 0; out: + securityfs_remove(digest_query_dentry); securityfs_remove(digest_label_dentry); securityfs_remove(digest_list_del_dentry); securityfs_remove(digest_list_add_dentry); From patchwork Mon Jul 26 16:36:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 486079 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=-13.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, URIBL_BLOCKED, USER_AGENT_GIT 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 0B8BAC4320A for ; Mon, 26 Jul 2021 16:41:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E6C896023B for ; Mon, 26 Jul 2021 16:41:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239744AbhGZQAi (ORCPT ); Mon, 26 Jul 2021 12:00:38 -0400 Received: from frasgout.his.huawei.com ([185.176.79.56]:3494 "EHLO frasgout.his.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239633AbhGZQAV (ORCPT ); Mon, 26 Jul 2021 12:00:21 -0400 Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.207]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4GYQJp6lCtz6GCVT; Tue, 27 Jul 2021 00:25:46 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2176.2; Mon, 26 Jul 2021 18:40:47 +0200 From: Roberto Sassu To: , , CC: , , , , , Roberto Sassu , kernel test robot Subject: [RFC][PATCH v2 10/12] diglim: Interfaces - digests_count Date: Mon, 26 Jul 2021 18:36:58 +0200 Message-ID: <20210726163700.2092768-11-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210726163700.2092768-1-roberto.sassu@huawei.com> References: <20210726163700.2092768-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml754-chm.china.huawei.com (10.201.108.204) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Introduce the digests_count interface, which shows the current number of digests stored in the hash table by type. Reported-by: kernel test robot (frame size warning) Signed-off-by: Roberto Sassu --- security/integrity/diglim/fs.c | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/security/integrity/diglim/fs.c b/security/integrity/diglim/fs.c index f1c1fc56448a..3b1d9616cb62 100644 --- a/security/integrity/diglim/fs.c +++ b/security/integrity/diglim/fs.c @@ -23,6 +23,7 @@ #include "diglim.h" #define MAX_DIGEST_LIST_SIZE (64 * 1024 * 1024 - 1) +#define TMPBUF_SIZE 512 static struct dentry *diglim_dir; /** @@ -36,6 +37,13 @@ static struct dentry *diglim_dir; * removed. */ static struct dentry *digest_lists_loaded_dir; +/** + * DOC: digests_count + * + * digests_count shows the current number of digests stored in the hash + * table by type. + */ +static struct dentry *digests_count; /** * DOC: digest_label * @@ -73,6 +81,39 @@ static struct dentry *digest_list_del_dentry; char digest_query[CRYPTO_MAX_ALG_NAME + 1 + IMA_MAX_DIGEST_SIZE * 2 + 1]; char digest_label[NAME_MAX + 1]; +static char *types_str[COMPACT__LAST] = { + [COMPACT_PARSER] = "Parser", + [COMPACT_FILE] = "File", + [COMPACT_METADATA] = "Metadata", + [COMPACT_DIGEST_LIST] = "Digest list", +}; + +static ssize_t diglim_show_htable_len(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + char *tmpbuf; + ssize_t ret, len = 0; + int i; + + tmpbuf = kmalloc(TMPBUF_SIZE, GFP_KERNEL); + if (!tmpbuf) + return -ENOMEM; + + for (i = COMPACT_PARSER; i < COMPACT__LAST; i++) + len += scnprintf(tmpbuf + len, TMPBUF_SIZE - len, + "%s digests: %lu\n", types_str[i], + htable[i].len); + + ret = simple_read_from_buffer(buf, count, ppos, tmpbuf, len); + kfree(tmpbuf); + return ret; +} + +static const struct file_operations htable_len_ops = { + .read = diglim_show_htable_len, + .llseek = generic_file_llseek, +}; + static int parse_digest_list_filename(const char *digest_list_filename, u8 *digest, enum hash_algo *algo) { @@ -696,6 +737,12 @@ static int __init diglim_fs_init(void) if (IS_ERR(digest_lists_loaded_dir)) goto out; + digests_count = securityfs_create_file("digests_count", 0440, + diglim_dir, NULL, + &htable_len_ops); + if (IS_ERR(digests_count)) + goto out; + digest_list_add_dentry = securityfs_create_file("digest_list_add", 0200, diglim_dir, NULL, &digest_list_upload_ops); @@ -726,6 +773,7 @@ static int __init diglim_fs_init(void) securityfs_remove(digest_label_dentry); securityfs_remove(digest_list_del_dentry); securityfs_remove(digest_list_add_dentry); + securityfs_remove(digests_count); securityfs_remove(digest_lists_loaded_dir); securityfs_remove(diglim_dir); return -1; From patchwork Mon Jul 26 16:37:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 486078 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=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT 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 338B3C43214 for ; Mon, 26 Jul 2021 16:41:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 20B7B6023B for ; Mon, 26 Jul 2021 16:41:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239979AbhGZQAq (ORCPT ); Mon, 26 Jul 2021 12:00:46 -0400 Received: from frasgout.his.huawei.com ([185.176.79.56]:3496 "EHLO frasgout.his.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239591AbhGZQAW (ORCPT ); Mon, 26 Jul 2021 12:00:22 -0400 Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.200]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4GYQNZ2fZLz6L9lV; Tue, 27 Jul 2021 00:29:02 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2176.2; Mon, 26 Jul 2021 18:40:48 +0200 From: Roberto Sassu To: , , CC: , , , , , Roberto Sassu Subject: [RFC][PATCH v2 12/12] diglim: Tests Date: Mon, 26 Jul 2021 18:37:00 +0200 Message-ID: <20210726163700.2092768-13-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210726163700.2092768-1-roberto.sassu@huawei.com> References: <20210726163700.2092768-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml754-chm.china.huawei.com (10.201.108.204) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Introduce a number of tests to ensure that DIGLIM works as expected: - digest_list_add_del_test_file_upload; - digest_list_add_del_test_file_upload_fault; - digest_list_add_del_test_buffer_upload; - digest_list_add_del_test_buffer_upload_fault; - digest_list_fuzzing_test; - digest_list_add_del_test_file_upload_measured; - digest_list_check_measurement_list_test_file_upload; - digest_list_check_measurement_list_test_buffer_upload. The tests are in tools/testing/selftests/diglim/selftest.c. A description of the tests can be found in Documentation/security/diglim/tests.rst. Signed-off-by: Roberto Sassu --- Documentation/security/diglim/index.rst | 1 + Documentation/security/diglim/tests.rst | 66 + MAINTAINERS | 2 + tools/testing/selftests/Makefile | 1 + tools/testing/selftests/diglim/Makefile | 19 + tools/testing/selftests/diglim/common.c | 115 ++ tools/testing/selftests/diglim/common.h | 31 + tools/testing/selftests/diglim/config | 3 + tools/testing/selftests/diglim/selftest.c | 1382 +++++++++++++++++++++ 9 files changed, 1620 insertions(+) create mode 100644 Documentation/security/diglim/tests.rst create mode 100644 tools/testing/selftests/diglim/Makefile create mode 100644 tools/testing/selftests/diglim/common.c create mode 100644 tools/testing/selftests/diglim/common.h create mode 100644 tools/testing/selftests/diglim/config create mode 100644 tools/testing/selftests/diglim/selftest.c diff --git a/Documentation/security/diglim/index.rst b/Documentation/security/diglim/index.rst index 0f28c5ad71c0..d4ba4ce50a59 100644 --- a/Documentation/security/diglim/index.rst +++ b/Documentation/security/diglim/index.rst @@ -11,3 +11,4 @@ Digest Lists Integrity Module (DIGLIM) architecture implementation remote_attestation + tests diff --git a/Documentation/security/diglim/tests.rst b/Documentation/security/diglim/tests.rst new file mode 100644 index 000000000000..d58ddbaeeb55 --- /dev/null +++ b/Documentation/security/diglim/tests.rst @@ -0,0 +1,66 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Testing +======= + +This section introduces a number of tests to ensure that DIGLIM works as +expected: + +- ``digest_list_add_del_test_file_upload``; +- ``digest_list_add_del_test_file_upload_fault``; +- ``digest_list_add_del_test_buffer_upload``; +- ``digest_list_add_del_test_buffer_upload_fault``; +- ``digest_list_fuzzing_test``; +- ``digest_list_add_del_test_file_upload_measured``; +- ``digest_list_check_measurement_list_test_file_upload``; +- ``digest_list_check_measurement_list_test_buffer_upload``. + +The tests are in ``tools/testing/selftests/diglim/selftest.c``. + +The first four tests randomly perform add, delete and query of digest +lists. They internally keep track at any time of the digest lists that are +currently uploaded to the kernel. + +Also, digest lists are generated randomly by selecting an arbitrary digest +algorithm and an arbitrary number of digests. To ensure a good number of +collisions, digests are a sequence of zeros, except for the first four +bytes that are set with a random number within a defined range. + +When a query operation is selected, a digest is chosen by getting another +random number within the same range. Then, the tests count how many times +the digest is found in the internally stored digest lists and in the query +result obtained from the kernel. The tests are successful if the obtained +numbers are the same. + +The ``file_upload`` variant creates a temporary file from a generated +digest list and sends its path to the kernel, so that the file is uploaded. +The ``buffer_upload`` variant directly sends the digest list buffer to the +kernel (it will be done by the user space parser after it converts a digest +list not in the compact format). + +The ``fault`` variant performs the test by enabling the ad-hoc fault +injection mechanism in the kernel (accessible through +``/fail_diglim``). The fault injection mechanism randomly injects +errors during the addition and deletion of digest lists. When an error +occurs, the rollback mechanism performs the reverse operation until the +point the error occurred, so that the kernel is left in the same state as +when the requested operation began. Since the kernel returns the error to +user space, the tests also know that the operation didn't succeed and +behave accordingly (they also revert the internal state). + +The fuzzing test simply sends randomly generated digest lists to the +kernel, to ensure that the parser is robust enough to handle malformed +data. + +The ``measured`` variant of the ``digest_list_add_del_test`` series checks +whether the digest lists actions are properly set after adding an IMA rule +to measure the digest lists. + +The ``digest_list_check_measurement_list_test`` tests verify the remote +attestation functionality. They verify whether IMA creates a measurement +entry for each addition and deletion of a digest list, and that the +deletion is forbidden if IMA created a measurement entry only for the +addition. + +The ``file_upload`` variant uploads a file, while the ``buffer_upload`` +variant uploads a buffer. diff --git a/MAINTAINERS b/MAINTAINERS index a7c502685109..e63061e63363 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5462,12 +5462,14 @@ F: Documentation/security/diglim/implementation.rst F: Documentation/security/diglim/index.rst F: Documentation/security/diglim/introduction.rst F: Documentation/security/diglim/remote_attestation.rst +F: Documentation/security/diglim/tests.rst F: include/linux/diglim.h F: include/uapi/linux/diglim.h F: security/integrity/diglim/diglim.h F: security/integrity/diglim/fs.c F: security/integrity/diglim/methods.c F: security/integrity/diglim/parser.c +F: tools/testing/selftests/diglim/ DIOLAN U2C-12 I2C DRIVER M: Guenter Roeck diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index fb010a35d61a..37460b308e07 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -8,6 +8,7 @@ TARGETS += clone3 TARGETS += core TARGETS += cpufreq TARGETS += cpu-hotplug +TARGETS += diglim TARGETS += drivers/dma-buf TARGETS += efivarfs TARGETS += exec diff --git a/tools/testing/selftests/diglim/Makefile b/tools/testing/selftests/diglim/Makefile new file mode 100644 index 000000000000..100c219955d7 --- /dev/null +++ b/tools/testing/selftests/diglim/Makefile @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +LDFLAGS += -lcrypto + +CFLAGS += -O2 -Wall -Wl,-no-as-needed -g -I./ -I../../../../usr/include/ \ + -L$(OUTPUT) -Wl,-rpath=./ -ggdb +LDLIBS += -lpthread + +OVERRIDE_TARGETS = 1 + +TEST_GEN_PROGS = selftest +TEST_GEN_PROGS_EXTENDED = libcommon.so + +include ../lib.mk + +$(OUTPUT)/libcommon.so: common.c + $(CC) $(CFLAGS) -shared -fPIC $< $(LDLIBS) -o $@ + +$(OUTPUT)/selftest: selftest.c $(TEST_GEN_PROGS_EXTENDED) + $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) -lcommon diff --git a/tools/testing/selftests/diglim/common.c b/tools/testing/selftests/diglim/common.c new file mode 100644 index 000000000000..88ff48fed308 --- /dev/null +++ b/tools/testing/selftests/diglim/common.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005,2006,2007,2008 IBM Corporation + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH + * + * Author: Roberto Sassu + * + * Common functions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +int write_buffer(char *path, char *buffer, size_t buffer_len, int uid) +{ + ssize_t to_write = buffer_len, written = 0; + int ret = 0, ret_seteuid, fd, cur_uid = geteuid(); + + fd = open(path, O_WRONLY); + if (fd < 0) + return -errno; + + if (uid >= 0) { + ret_seteuid = seteuid(uid); + if (ret_seteuid < 0) + return ret_seteuid; + } + + while (to_write) { + written = write(fd, buffer + buffer_len - to_write, to_write); + if (written <= 0) { + ret = -errno; + break; + } + + to_write -= written; + } + + if (uid >= 0) { + ret_seteuid = seteuid(cur_uid); + if (ret_seteuid < 0) + return ret_seteuid; + } + + close(fd); + return ret; +} + +int read_buffer(char *path, char **buffer, size_t *buffer_len, bool alloc, + bool is_char) +{ + ssize_t len = 0, read_len; + int ret = 0, fd; + + fd = open(path, O_RDONLY); + if (fd < 0) + return -errno; + + if (alloc) { + *buffer = NULL; + *buffer_len = 0; + } + + while (1) { + if (alloc) { + if (*buffer_len == len) { + *buffer_len += BUFFER_SIZE; + *buffer = realloc(*buffer, *buffer_len + 1); + if (!*buffer) { + ret = -ENOMEM; + goto out; + } + } + } + + read_len = read(fd, *buffer + len, *buffer_len - len); + if (read_len < 0) { + ret = -errno; + goto out; + } + + if (!read_len) + break; + + len += read_len; + } + + *buffer_len = len; + if (is_char) + (*buffer)[(*buffer_len)++] = '\0'; +out: + close(fd); + if (ret < 0) { + if (alloc) { + free(*buffer); + *buffer = NULL; + } + } + + return ret; +} diff --git a/tools/testing/selftests/diglim/common.h b/tools/testing/selftests/diglim/common.h new file mode 100644 index 000000000000..d3e5808c06e8 --- /dev/null +++ b/tools/testing/selftests/diglim/common.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005,2006,2007,2008 IBM Corporation + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH + * + * Author: Roberto Sassu + * + * Header of common.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFFER_SIZE 1024 + +int write_buffer(char *path, char *buffer, size_t buffer_len, int uid); +int read_buffer(char *path, char **buffer, size_t *buffer_len, bool alloc, + bool is_char); diff --git a/tools/testing/selftests/diglim/config b/tools/testing/selftests/diglim/config new file mode 100644 index 000000000000..faafc742974c --- /dev/null +++ b/tools/testing/selftests/diglim/config @@ -0,0 +1,3 @@ +CONFIG_DIGEST_LISTS=y +CONFIG_FAULT_INJECTION=y +CONFIG_FAULT_INJECTION_DEBUG_FS=y diff --git a/tools/testing/selftests/diglim/selftest.c b/tools/testing/selftests/diglim/selftest.c new file mode 100644 index 000000000000..efcd83409b39 --- /dev/null +++ b/tools/testing/selftests/diglim/selftest.c @@ -0,0 +1,1382 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005,2006,2007,2008 IBM Corporation + * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH + * + * Author: Roberto Sassu + * + * Functions to test DIGLIM. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common.h" +#include "../kselftest_harness.h" + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +#define MD5_DIGEST_SIZE 16 +#define SHA1_DIGEST_SIZE 20 +#define RMD160_DIGEST_SIZE 20 +#define SHA256_DIGEST_SIZE 32 +#define SHA384_DIGEST_SIZE 48 +#define SHA512_DIGEST_SIZE 64 +#define SHA224_DIGEST_SIZE 28 +#define RMD128_DIGEST_SIZE 16 +#define RMD256_DIGEST_SIZE 32 +#define RMD320_DIGEST_SIZE 40 +#define WP256_DIGEST_SIZE 32 +#define WP384_DIGEST_SIZE 48 +#define WP512_DIGEST_SIZE 64 +#define TGR128_DIGEST_SIZE 16 +#define TGR160_DIGEST_SIZE 20 +#define TGR192_DIGEST_SIZE 24 +#define SM3256_DIGEST_SIZE 32 +#define STREEBOG256_DIGEST_SIZE 32 +#define STREEBOG512_DIGEST_SIZE 64 + +#define DIGEST_LIST_PATH_TEMPLATE "/tmp/digest_list.XXXXXX" + +#define INTEGRITY_DIR "/sys/kernel/security/integrity" + +#define DIGEST_LIST_DIR INTEGRITY_DIR "/diglim" +#define DIGEST_QUERY_PATH DIGEST_LIST_DIR "/digest_query" +#define DIGEST_LABEL_PATH DIGEST_LIST_DIR "/digest_label" +#define DIGEST_LIST_ADD_PATH DIGEST_LIST_DIR "/digest_list_add" +#define DIGEST_LIST_DEL_PATH DIGEST_LIST_DIR "/digest_list_del" +#define DIGEST_LISTS_LOADED_PATH DIGEST_LIST_DIR "/digest_lists_loaded" +#define DIGESTS_COUNT DIGEST_LIST_DIR "/digests_count" + +#define IMA_POLICY_PATH INTEGRITY_DIR "/ima/policy" +#define IMA_MEASUREMENTS_PATH INTEGRITY_DIR "/ima/ascii_runtime_measurements" + +#define DIGEST_LIST_DEBUGFS_DIR "/sys/kernel/debug/fail_diglim" +#define DIGEST_LIST_DEBUGFS_TASK_FILTER DIGEST_LIST_DEBUGFS_DIR "/task-filter" +#define DIGEST_LIST_DEBUGFS_PROBABILITY DIGEST_LIST_DEBUGFS_DIR "/probability" +#define DIGEST_LIST_DEBUGFS_TIMES DIGEST_LIST_DEBUGFS_DIR "/times" +#define DIGEST_LIST_DEBUGFS_VERBOSE DIGEST_LIST_DEBUGFS_DIR "/verbose" +#define PROCFS_SELF_FAULT "/proc/self/make-it-fail" + +#define MAX_LINE_LENGTH 512 +#define LABEL_LEN 32 +#define MAX_DIGEST_COUNT 100 +#define MAX_DIGEST_LISTS 100 +#define MAX_DIGEST_BLOCKS 10 +#define MAX_DIGEST_VALUE 10 +#define MAX_SEARCH_ATTEMPTS 10 +#define NUM_QUERIES 1000 +#define MAX_DIGEST_LIST_SIZE 10000 +#define NUM_ITERATIONS 100000 + +enum upload_types { UPLOAD_FILE, UPLOAD_BUFFER }; + +const char *const hash_algo_name[HASH_ALGO__LAST] = { + [HASH_ALGO_MD4] = "md4", + [HASH_ALGO_MD5] = "md5", + [HASH_ALGO_SHA1] = "sha1", + [HASH_ALGO_RIPE_MD_160] = "rmd160", + [HASH_ALGO_SHA256] = "sha256", + [HASH_ALGO_SHA384] = "sha384", + [HASH_ALGO_SHA512] = "sha512", + [HASH_ALGO_SHA224] = "sha224", + [HASH_ALGO_RIPE_MD_128] = "rmd128", + [HASH_ALGO_RIPE_MD_256] = "rmd256", + [HASH_ALGO_RIPE_MD_320] = "rmd320", + [HASH_ALGO_WP_256] = "wp256", + [HASH_ALGO_WP_384] = "wp384", + [HASH_ALGO_WP_512] = "wp512", + [HASH_ALGO_TGR_128] = "tgr128", + [HASH_ALGO_TGR_160] = "tgr160", + [HASH_ALGO_TGR_192] = "tgr192", + [HASH_ALGO_SM3_256] = "sm3", + [HASH_ALGO_STREEBOG_256] = "streebog256", + [HASH_ALGO_STREEBOG_512] = "streebog512", +}; + +const int hash_digest_size[HASH_ALGO__LAST] = { + [HASH_ALGO_MD4] = MD5_DIGEST_SIZE, + [HASH_ALGO_MD5] = MD5_DIGEST_SIZE, + [HASH_ALGO_SHA1] = SHA1_DIGEST_SIZE, + [HASH_ALGO_RIPE_MD_160] = RMD160_DIGEST_SIZE, + [HASH_ALGO_SHA256] = SHA256_DIGEST_SIZE, + [HASH_ALGO_SHA384] = SHA384_DIGEST_SIZE, + [HASH_ALGO_SHA512] = SHA512_DIGEST_SIZE, + [HASH_ALGO_SHA224] = SHA224_DIGEST_SIZE, + [HASH_ALGO_RIPE_MD_128] = RMD128_DIGEST_SIZE, + [HASH_ALGO_RIPE_MD_256] = RMD256_DIGEST_SIZE, + [HASH_ALGO_RIPE_MD_320] = RMD320_DIGEST_SIZE, + [HASH_ALGO_WP_256] = WP256_DIGEST_SIZE, + [HASH_ALGO_WP_384] = WP384_DIGEST_SIZE, + [HASH_ALGO_WP_512] = WP512_DIGEST_SIZE, + [HASH_ALGO_TGR_128] = TGR128_DIGEST_SIZE, + [HASH_ALGO_TGR_160] = TGR160_DIGEST_SIZE, + [HASH_ALGO_TGR_192] = TGR192_DIGEST_SIZE, + [HASH_ALGO_SM3_256] = SM3256_DIGEST_SIZE, + [HASH_ALGO_STREEBOG_256] = STREEBOG256_DIGEST_SIZE, + [HASH_ALGO_STREEBOG_512] = STREEBOG512_DIGEST_SIZE, +}; + +struct digest_list_item { + unsigned long long size; + u8 *buf; + u8 actions; + char digest_str[64 * 2 + 1]; + enum hash_algo algo; + char filename_suffix[6 + 1]; +}; + +static const char hex_asc[] = "0123456789abcdef"; + +#define hex_asc_lo(x) hex_asc[((x) & 0x0f)] +#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4] + +static inline char *hex_byte_pack(char *buf, unsigned char byte) +{ + *buf++ = hex_asc_hi(byte); + *buf++ = hex_asc_lo(byte); + return buf; +} + +/* from lib/hexdump.c (Linux kernel) */ +static int hex_to_bin(char ch) +{ + if ((ch >= '0') && (ch <= '9')) + return ch - '0'; + ch = tolower(ch); + if ((ch >= 'a') && (ch <= 'f')) + return ch - 'a' + 10; + return -1; +} + +int _hex2bin(unsigned char *dst, const char *src, size_t count) +{ + while (count--) { + int hi = hex_to_bin(*src++); + int lo = hex_to_bin(*src++); + + if ((hi < 0) || (lo < 0)) + return -1; + + *dst++ = (hi << 4) | lo; + } + return 0; +} + +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; +} + +u32 num_max_digest_lists = MAX_DIGEST_LISTS; +u32 digest_lists_pos; +struct digest_list_item *digest_lists[MAX_DIGEST_LISTS]; + +enum hash_algo ima_hash_algo = HASH_ALGO__LAST; + +static enum hash_algo get_ima_hash_algo(void) +{ + char *measurement_list, *measurement_list_ptr; + size_t measurement_list_len; + int ret, i = 0; + + if (ima_hash_algo != HASH_ALGO__LAST) + return ima_hash_algo; + + ret = read_buffer(IMA_MEASUREMENTS_PATH, &measurement_list, + &measurement_list_len, true, true); + if (ret < 0) + return HASH_ALGO_SHA256; + + measurement_list_ptr = measurement_list; + while ((strsep(&measurement_list_ptr, " ")) && i++ < 2) + ; + + for (i = 0; i < HASH_ALGO__LAST; i++) { + if (!strncmp(hash_algo_name[i], measurement_list_ptr, + strlen(hash_algo_name[i]))) { + ima_hash_algo = i; + break; + } + } + + free(measurement_list); + return ima_hash_algo; +} + +int calc_digest(u8 *digest, void *data, u64 len, enum hash_algo algo) +{ + EVP_MD_CTX *mdctx; + const EVP_MD *md; + int ret = -EINVAL; + + OpenSSL_add_all_algorithms(); + + md = EVP_get_digestbyname(hash_algo_name[algo]); + if (!md) + goto out; + + mdctx = EVP_MD_CTX_create(); + if (!mdctx) + goto out; + + if (EVP_DigestInit_ex(mdctx, md, NULL) != 1) + goto out_mdctx; + + if (EVP_DigestUpdate(mdctx, data, len) != 1) + goto out_mdctx; + + if (EVP_DigestFinal_ex(mdctx, digest, NULL) != 1) + goto out_mdctx; + + ret = 0; +out_mdctx: + EVP_MD_CTX_destroy(mdctx); +out: + EVP_cleanup(); + return ret; +} + +int calc_file_digest(u8 *digest, char *path, enum hash_algo algo) +{ + void *data = MAP_FAILED; + struct stat st; + int fd, ret = 0; + + if (stat(path, &st) == -1) + return -EACCES; + + fd = open(path, O_RDONLY); + if (fd < 0) + return -errno; + + if (st.st_size) { + data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (data == MAP_FAILED) { + ret = -ENOMEM; + goto out; + } + } + + ret = calc_digest(digest, data, st.st_size, algo); +out: + if (data != MAP_FAILED) + munmap(data, st.st_size); + + close(fd); + return ret; +} + +static struct digest_list_item *digest_list_generate(void) +{ + struct digest_list_item *digest_list; + struct compact_list_hdr *hdr_array = NULL; + u8 *buf_ptr; + u32 num_digest_blocks = 0; + u8 digest[64]; + int ret, i, j; + + digest_list = calloc(1, sizeof(*digest_list)); + if (!digest_list) + return NULL; + + while (!num_digest_blocks) { + ret = getrandom(&num_digest_blocks, + sizeof(num_digest_blocks), 0); + if (ret < 0) + goto out; + + num_digest_blocks = num_digest_blocks % MAX_DIGEST_BLOCKS; + } + + hdr_array = calloc(num_digest_blocks, sizeof(*hdr_array)); + if (!hdr_array) + goto out; + + for (i = 0; i < num_digest_blocks; i++) { + ret = getrandom(&hdr_array[i], sizeof(hdr_array[i]), 0); + if (ret < 0) + goto out; + + hdr_array[i].version = 1; + hdr_array[i]._reserved = 0; + /* COMPACT_DIGEST_LIST type is not allowed. */ + hdr_array[i].type = hdr_array[i].type % (COMPACT__LAST - 1); + hdr_array[i].modifiers = + hdr_array[i].modifiers % (1 << COMPACT_MOD_IMMUTABLE) + 1; + hdr_array[i].algo = hdr_array[i].algo % HASH_ALGO_RIPE_MD_128; + hdr_array[i].count = hdr_array[i].count % MAX_DIGEST_COUNT; + + while (!hdr_array[i].count) { + ret = getrandom(&hdr_array[i].count, + sizeof(hdr_array[i].count), 0); + if (ret < 0) + goto out; + + hdr_array[i].count = + hdr_array[i].count % MAX_DIGEST_COUNT; + } + + hdr_array[i].datalen = + hdr_array[i].count * hash_digest_size[hdr_array[i].algo]; + + digest_list->size += sizeof(*hdr_array) + hdr_array[i].datalen; + } + + digest_list->buf = calloc(digest_list->size, sizeof(unsigned char)); + if (!digest_list->buf) { + free(digest_list); + ret = -ENOMEM; + goto out; + } + + buf_ptr = digest_list->buf; + + for (i = 0; i < num_digest_blocks; i++) { + memcpy(buf_ptr, &hdr_array[i], sizeof(*hdr_array)); + buf_ptr += sizeof(*hdr_array); + + for (j = 0; j < hdr_array[i].count; j++) { + ret = getrandom(buf_ptr, sizeof(u32), 0); + if (ret < 0) + goto out; + + *(u32 *)buf_ptr = *(u32 *)buf_ptr % MAX_DIGEST_VALUE; + buf_ptr += hash_digest_size[hdr_array[i].algo]; + } + } + + digest_list->algo = get_ima_hash_algo(); + if (digest_list->algo == HASH_ALGO__LAST) { + ret = -ENOENT; + goto out; + } + + ret = calc_digest(digest, digest_list->buf, digest_list->size, + digest_list->algo); + if (ret < 0) + goto out; + + _bin2hex(digest_list->digest_str, digest, + hash_digest_size[digest_list->algo]); + + ret = 0; +out: + if (ret < 0) { + free(digest_list->buf); + free(digest_list); + } + + free(hdr_array); + return !ret ? digest_list : NULL; +} + +static struct digest_list_item *digest_list_generate_random(void) +{ + struct digest_list_item *digest_list; + struct compact_list_hdr *hdr; + u32 size = 0; + u8 digest[64]; + int ret; + + digest_list = calloc(1, sizeof(*digest_list)); + if (!digest_list) + return NULL; + + while (!size) { + ret = getrandom(&size, sizeof(size), 0); + if (ret < 0) + goto out; + + size = size % MAX_DIGEST_LIST_SIZE; + } + + digest_list->size = size; + digest_list->buf = calloc(digest_list->size, sizeof(unsigned char)); + if (!digest_list->buf) { + free(digest_list); + ret = -ENOMEM; + goto out; + } + + ret = getrandom(digest_list->buf, digest_list->size, 0); + if (ret < 0) + goto out; + + hdr = (struct compact_list_hdr *)digest_list->buf; + hdr->version = 1; + hdr->_reserved = 0; + hdr->type = hdr->type % (COMPACT__LAST - 1); + hdr->algo = hdr->algo % HASH_ALGO__LAST; + + digest_list->algo = get_ima_hash_algo(); + if (digest_list->algo == HASH_ALGO__LAST) { + ret = -ENOENT; + goto out; + } + + ret = calc_digest(digest, digest_list->buf, digest_list->size, + digest_list->algo); + if (ret < 0) + goto out; + + _bin2hex(digest_list->digest_str, digest, + hash_digest_size[digest_list->algo]); + + ret = 0; +out: + if (ret < 0) { + free(digest_list->buf); + free(digest_list); + } + + return !ret ? digest_list : NULL; +} + +static int digest_list_upload(struct digest_list_item *digest_list, enum ops op, + enum upload_types upload_type, int uid) +{ + char path_template[] = DIGEST_LIST_PATH_TEMPLATE; + char *path_upload = DIGEST_LIST_ADD_PATH, *basename; + unsigned char *buffer = digest_list->buf; + size_t buffer_len = digest_list->size; + unsigned char rnd[3]; + int ret = 0, fd; + + if (op == DIGEST_LIST_ADD) { + if (upload_type == UPLOAD_FILE) { + fd = mkstemp(path_template); + if (fd < 0) + return -EPERM; + + fchmod(fd, 0644); + close(fd); + ret = write_buffer(path_template, + (char *)digest_list->buf, + digest_list->size, -1); + if (ret < 0) + goto out; + + buffer = (unsigned char *)path_template; + buffer_len = strlen(path_template); + } else { + ret = getrandom(rnd, sizeof(rnd), 0); + if (ret < 0) + goto out; + + _bin2hex(path_template + + sizeof(DIGEST_LIST_PATH_TEMPLATE) - 7, rnd, + sizeof(rnd)); + } + + memcpy(digest_list->filename_suffix, + path_template + sizeof(DIGEST_LIST_PATH_TEMPLATE) - 7, + 6); + } else { + memcpy(path_template + sizeof(DIGEST_LIST_PATH_TEMPLATE) - 7, + digest_list->filename_suffix, 6); + path_upload = DIGEST_LIST_DEL_PATH; + if (upload_type == UPLOAD_FILE) { + buffer = (unsigned char *)path_template; + buffer_len = strlen(path_template); + } + } + + if (upload_type == UPLOAD_BUFFER) { + basename = strrchr(path_template, '/') + 1; + ret = write_buffer(DIGEST_LABEL_PATH, basename, + strlen(basename), -1); + if (ret < 0) + goto out; + } + + ret = write_buffer(path_upload, (char *)buffer, buffer_len, uid); +out: + if ((op == DIGEST_LIST_ADD && ret < 0) || + (op == DIGEST_LIST_DEL && !ret)) + unlink(path_template); + + return ret; +} + +static int digest_list_check(struct digest_list_item *digest_list, enum ops op) +{ + char path[PATH_MAX]; + u8 digest_list_buf[MAX_LINE_LENGTH]; + char digest_list_info[MAX_LINE_LENGTH]; + ssize_t size = digest_list->size; + struct compact_list_hdr *hdr; + struct stat st; + int ret = 0, i, fd, path_len, len, read_len; + + path_len = snprintf(path, sizeof(path), "%s/%s-%s-digest_list.%s.ascii", + DIGEST_LISTS_LOADED_PATH, + hash_algo_name[digest_list->algo], + digest_list->digest_str, + digest_list->filename_suffix); + + path[path_len - 6] = '\0'; + + if (op == DIGEST_LIST_DEL) { + if (stat(path, &st) != -1) + return -EEXIST; + + path[path_len - 6] = '.'; + + if (stat(path, &st) != -1) + return -EEXIST; + + return 0; + } + + fd = open(path, O_RDONLY); + if (fd < 0) + return -errno; + + while (size) { + len = read(fd, digest_list_buf, sizeof(digest_list_buf)); + if (len <= 0) { + ret = -errno; + goto out; + } + + if (memcmp(digest_list_buf, + digest_list->buf + digest_list->size - size, len)) { + ret = -EINVAL; + goto out; + } + + size -= len; + } + + close(fd); + + path[path_len - 6] = '.'; + + fd = open(path, O_RDONLY); + if (fd < 0) + return -errno; + + size = digest_list->size; + while (size) { + hdr = (struct compact_list_hdr *)(digest_list->buf + + digest_list->size - size); + + /* From digest_list_show_common(). */ + len = snprintf(digest_list_info, sizeof(digest_list_info), + "actions: %d, version: %d, algo: %s, type: %d, modifiers: %d, count: %d, datalen: %d\n", + digest_list->actions, hdr->version, + hash_algo_name[hdr->algo], hdr->type, hdr->modifiers, + hdr->count, hdr->datalen); + + read_len = read(fd, digest_list_buf, len); + + if (read_len != len || + memcmp(digest_list_info, digest_list_buf, len)) { + ret = -EIO; + goto out; + } + + size -= sizeof(*hdr); + + for (i = 0; i < hdr->count; i++) { + _bin2hex(digest_list_info, + digest_list->buf + digest_list->size - size, + hash_digest_size[hdr->algo]); + + read_len = read(fd, digest_list_buf, + hash_digest_size[hdr->algo] * 2 + 1); + + if (read_len != hash_digest_size[hdr->algo] * 2 + 1 || + memcmp(digest_list_info, digest_list_buf, + read_len - 1) || + digest_list_buf[read_len - 1] != '\n') { + ret = -EIO; + goto out; + } + + size -= hash_digest_size[hdr->algo]; + } + } +out: + close(fd); + return ret; +} + +static int digest_list_query(u8 *digest, enum hash_algo algo, + char **query_result) +{ + ssize_t len, to_write, written; + char query[256] = { 0 }; + size_t query_result_len; + int ret = 0, fd; + + len = snprintf(query, sizeof(query), "%s-", hash_algo_name[algo]); + + _bin2hex(query + len, digest, hash_digest_size[algo]); + len += hash_digest_size[algo] * 2 + 1; + + fd = open(DIGEST_QUERY_PATH, O_WRONLY); + if (fd < 0) + return -errno; + + to_write = len; + + while (to_write) { + written = write(fd, query + len - to_write, to_write); + if (written <= 0) { + ret = -errno; + break; + } + + to_write -= written; + } + + close(fd); + if (ret < 0) + return ret; + + return read_buffer(DIGEST_QUERY_PATH, query_result, &query_result_len, + true, true); +} + +static int *get_count_gen_lists(u8 *digest, enum hash_algo algo, + bool is_digest_list) +{ + struct compact_list_hdr *hdr; + u8 *buf_ptr; + unsigned long long size; + struct digest_list_item *digest_list; + u8 digest_list_digest[64]; + int i, j, *count; + + count = calloc(num_max_digest_lists, sizeof(*count)); + if (!count) + return count; + + for (i = 0; i < num_max_digest_lists; i++) { + if (!digest_lists[i]) + continue; + + digest_list = digest_lists[i]; + size = digest_lists[i]->size; + buf_ptr = digest_lists[i]->buf; + + if (is_digest_list) { + _hex2bin(digest_list_digest, digest_list->digest_str, + hash_digest_size[digest_list->algo]); + if (!memcmp(digest_list_digest, digest, + hash_digest_size[digest_list->algo])) + count[i]++; + + continue; + } + + while (size) { + hdr = (struct compact_list_hdr *)buf_ptr; + if (hdr->algo != algo) { + buf_ptr += sizeof(*hdr) + hdr->datalen; + size -= sizeof(*hdr) + hdr->datalen; + continue; + } + + buf_ptr += sizeof(*hdr); + size -= sizeof(*hdr); + + for (j = 0; j < hdr->count; j++) { + if (!memcmp(digest, buf_ptr, + hash_digest_size[algo])) + count[i]++; + buf_ptr += hash_digest_size[algo]; + size -= hash_digest_size[algo]; + } + } + } + + return count; +} + +static int *get_count_kernel_query(u8 *digest, enum hash_algo algo, + bool is_digest_list) +{ + char *query_result = NULL, *query_result_ptr, *line; + char digest_list_info[MAX_LINE_LENGTH]; + char label[256]; + struct compact_list_hdr *hdr; + struct digest_list_item *digest_list; + unsigned long long size, size_info; + int ret, i, *count = NULL; + + count = calloc(num_max_digest_lists, sizeof(*count)); + if (!count) + return count; + + ret = digest_list_query(digest, algo, &query_result); + if (ret < 0) + goto out; + + query_result_ptr = query_result; + + while ((line = strsep(&query_result_ptr, "\n"))) { + if (!strlen(line)) + continue; + + for (i = 0; i < num_max_digest_lists; i++) { + if (!digest_lists[i]) + continue; + + digest_list = digest_lists[i]; + size = digest_list->size; + + if (is_digest_list) { + snprintf(label, sizeof(label), + "%s-%s-digest_list.%s", + hash_algo_name[digest_list->algo], + digest_list->digest_str, + digest_list->filename_suffix); + + /* From digest_query_show(). */ + size_info = snprintf(digest_list_info, + sizeof(digest_list_info), + "%s (actions: %d): type: %d, size: %lld\n", + label, digest_list->actions, + COMPACT_DIGEST_LIST, size); + + /* strsep() replaced '\n' with '\0' in line. */ + digest_list_info[size_info - 1] = '\0'; + + if (!strcmp(digest_list_info, line)) + count[i]++; + + continue; + } + + while (size) { + hdr = + (struct compact_list_hdr *)(digest_list->buf + + digest_list->size - size); + size -= sizeof(*hdr) + hdr->datalen; + + snprintf(label, sizeof(label), + "%s-%s-digest_list.%s", + hash_algo_name[digest_list->algo], + digest_list->digest_str, + digest_list->filename_suffix); + + /* From digest_query_show(). */ + size_info = snprintf(digest_list_info, + sizeof(digest_list_info), + "%s (actions: %d): version: %d, algo: %s, type: %d, modifiers: %d, count: %d, datalen: %d\n", + label, digest_list->actions, + hdr->version, + hash_algo_name[hdr->algo], hdr->type, + hdr->modifiers, hdr->count, + hdr->datalen); + + /* strsep() replaced '\n' with '\0' in line. */ + digest_list_info[size_info - 1] = '\0'; + + if (!strcmp(digest_list_info, line)) { + count[i]++; + break; + } + } + } + } +out: + free(query_result); + if (ret < 0) + free(count); + + return (!ret) ? count : NULL; +} + +static int compare_count(u8 *digest, enum hash_algo algo, + bool is_digest_list, struct __test_metadata *_metadata) +{ + int *count_gen_list_array, *count_kernel_query_array; + int count_gen_list = 0, count_kernel_query = 0; + char digest_str[64 * 2 + 1] = { 0 }; + int i; + + count_gen_list_array = get_count_gen_lists(digest, algo, + is_digest_list); + if (!count_gen_list_array) + return -EINVAL; + + count_kernel_query_array = get_count_kernel_query(digest, algo, + is_digest_list); + if (!count_kernel_query_array) { + free(count_gen_list_array); + return -EINVAL; + } + + for (i = 0; i < num_max_digest_lists; i++) { + count_gen_list += count_gen_list_array[i]; + count_kernel_query += count_kernel_query_array[i]; + } + + _bin2hex(digest_str, digest, hash_digest_size[algo]); + + TH_LOG("digest: %s, algo: %s, gen list digests: %d, kernel digests: %d", + digest_str, hash_algo_name[algo], count_gen_list, + count_kernel_query); + free(count_gen_list_array); + free(count_kernel_query_array); + return (count_gen_list == count_kernel_query) ? 0 : -EINVAL; +} + +static void digest_list_delete_all(struct __test_metadata *_metadata, + enum upload_types upload_type) +{ + int ret, i; + + for (i = 0; i < MAX_DIGEST_LISTS; i++) { + if (!digest_lists[i]) + continue; + + ret = digest_list_upload(digest_lists[i], DIGEST_LIST_DEL, + upload_type, -1); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_upload() failed\n"); + } + + free(digest_lists[i]->buf); + free(digest_lists[i]); + digest_lists[i] = NULL; + } +} + +FIXTURE(test) +{ + enum upload_types upload_type; +}; + +FIXTURE_SETUP(test) +{ +} + +FIXTURE_TEARDOWN(test) +{ + digest_list_delete_all(_metadata, self->upload_type); +} + +static int enable_fault_injection(void) +{ + int ret; + + ret = write_buffer(DIGEST_LIST_DEBUGFS_TASK_FILTER, "Y", 1, -1); + if (ret < 0) + return ret; + + ret = write_buffer(DIGEST_LIST_DEBUGFS_PROBABILITY, "1", 1, -1); + if (ret < 0) + return ret; + + ret = write_buffer(DIGEST_LIST_DEBUGFS_TIMES, "10000", 5, -1); + if (ret < 0) + return ret; + + ret = write_buffer(DIGEST_LIST_DEBUGFS_VERBOSE, "1", 1, -1); + if (ret < 0) + return ret; + + ret = write_buffer(PROCFS_SELF_FAULT, "1", 1, -1); + if (ret < 0) + return ret; + + return 0; +} + +static void digest_list_add_del_test(struct __test_metadata *_metadata, + int fault_injection, + enum upload_types upload_type) +{ + u32 value; + enum ops op; + enum hash_algo algo; + u8 digest[64]; + int ret, i, cur_queries = 1; + + while (cur_queries <= NUM_QUERIES) { + ret = getrandom(&op, 1, 0); + ASSERT_EQ(1, ret) { + TH_LOG("getrandom() failed\n"); + } + + op = op % 2; + + switch (op) { + case DIGEST_LIST_ADD: + TH_LOG("add digest list..."); + for (digest_lists_pos = 0; + digest_lists_pos < num_max_digest_lists; + digest_lists_pos++) + if (!digest_lists[digest_lists_pos]) + break; + + if (digest_lists_pos == num_max_digest_lists) + continue; + + digest_lists[digest_lists_pos] = digest_list_generate(); + ASSERT_NE(NULL, digest_lists[digest_lists_pos]) { + TH_LOG("digest_list_generate() failed"); + } + + ret = digest_list_upload(digest_lists[digest_lists_pos], + op, upload_type, -1); + /* Handle failures from fault injection. */ + if (fault_injection && ret < 0) { + TH_LOG("handle failure..."); + ret = digest_list_check( + digest_lists[digest_lists_pos], + DIGEST_LIST_DEL); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_check() failed"); + } + + free(digest_lists[digest_lists_pos]->buf); + free(digest_lists[digest_lists_pos]); + digest_lists[digest_lists_pos] = NULL; + break; + } + + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_upload() failed"); + } + + ret = digest_list_check(digest_lists[digest_lists_pos], + op); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_check() failed"); + } + + break; + case DIGEST_LIST_DEL: + TH_LOG("delete digest list..."); + for (digest_lists_pos = 0; + digest_lists_pos < num_max_digest_lists; + digest_lists_pos++) + if (digest_lists[digest_lists_pos]) + break; + + if (digest_lists_pos == num_max_digest_lists) + continue; + + for (i = 0; i < MAX_SEARCH_ATTEMPTS; i++) { + ret = getrandom(&digest_lists_pos, + sizeof(digest_lists_pos), 0); + ASSERT_EQ(sizeof(digest_lists_pos), ret) { + TH_LOG("getrandom() failed"); + } + + digest_lists_pos = + digest_lists_pos % num_max_digest_lists; + + if (digest_lists[digest_lists_pos]) + break; + } + + if (i == MAX_SEARCH_ATTEMPTS) { + for (digest_lists_pos = 0; + digest_lists_pos < num_max_digest_lists; + digest_lists_pos++) + if (digest_lists[digest_lists_pos]) + break; + + if (digest_lists_pos == num_max_digest_lists) + continue; + } + + ret = digest_list_upload(digest_lists[digest_lists_pos], + op, upload_type, -1); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_upload() failed"); + } + + ret = digest_list_check(digest_lists[digest_lists_pos], + op); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_check() failed"); + } + + free(digest_lists[digest_lists_pos]->buf); + free(digest_lists[digest_lists_pos]); + digest_lists[digest_lists_pos] = NULL; + break; + default: + break; + } + + ret = getrandom(&value, sizeof(value), 0); + ASSERT_EQ(sizeof(value), ret) { + TH_LOG("getrandom() failed"); + } + + value = value % 10; + + if (value != 1) + continue; + + ret = getrandom(&value, sizeof(value), 0); + ASSERT_EQ(sizeof(value), ret) { + TH_LOG("getrandom() failed"); + } + + value = value % MAX_DIGEST_VALUE; + + ret = getrandom(&algo, sizeof(algo), 0); + ASSERT_EQ(sizeof(algo), ret) { + TH_LOG("getrandom() failed"); + } + + algo = algo % HASH_ALGO_RIPE_MD_128; + + memset(digest, 0, sizeof(digest)); + *(u32 *)digest = value; + + ret = compare_count(digest, algo, false, _metadata); + ASSERT_EQ(0, ret) { + TH_LOG("count mismatch"); + } + + ret = getrandom(&value, sizeof(value), 0); + ASSERT_EQ(sizeof(value), ret) { + TH_LOG("getrandom() failed"); + } + + value = value % MAX_DIGEST_LISTS; + + if (digest_lists[value] != NULL) { + _hex2bin(digest, digest_lists[value]->digest_str, + hash_digest_size[digest_lists[value]->algo]); + + ret = compare_count(digest, digest_lists[value]->algo, + true, _metadata); + ASSERT_EQ(0, ret) { + TH_LOG("count mismatch"); + } + } + + TH_LOG("query digest lists (%d/%d)...", cur_queries, + NUM_QUERIES); + + cur_queries++; + } +} + +TEST_F_TIMEOUT(test, digest_list_add_del_test_file_upload, UINT_MAX) +{ + self->upload_type = UPLOAD_FILE; + digest_list_add_del_test(_metadata, 0, self->upload_type); +} + +TEST_F_TIMEOUT(test, digest_list_add_del_test_file_upload_fault, UINT_MAX) +{ + int ret; + + self->upload_type = UPLOAD_FILE; + + ret = enable_fault_injection(); + ASSERT_EQ(0, ret) { + TH_LOG("enable_fault_injection() failed"); + } + + digest_list_add_del_test(_metadata, 1, self->upload_type); +} + +TEST_F_TIMEOUT(test, digest_list_add_del_test_buffer_upload, UINT_MAX) +{ + self->upload_type = UPLOAD_BUFFER; + digest_list_add_del_test(_metadata, 0, self->upload_type); +} + +TEST_F_TIMEOUT(test, digest_list_add_del_test_buffer_upload_fault, UINT_MAX) +{ + int ret; + + self->upload_type = UPLOAD_BUFFER; + + ret = enable_fault_injection(); + ASSERT_EQ(0, ret) { + TH_LOG("enable_fault_injection() failed"); + } + + digest_list_add_del_test(_metadata, 1, self->upload_type); +} + +FIXTURE(test_fuzzing) +{ +}; + +FIXTURE_SETUP(test_fuzzing) +{ +} + +FIXTURE_TEARDOWN(test_fuzzing) +{ +} + +TEST_F_TIMEOUT(test_fuzzing, digest_list_fuzzing_test, UINT_MAX) +{ + char digests_count_before[256] = { 0 }; + char *digests_count_before_ptr = digests_count_before; + char digests_count_after[256] = { 0 }; + char *digests_count_after_ptr = digests_count_after; + size_t len = sizeof(digests_count_before) - 1; + int ret, i; + + ret = read_buffer(DIGESTS_COUNT, &digests_count_before_ptr, &len, + false, true); + ASSERT_EQ(0, ret) { + TH_LOG("read_buffer() failed"); + } + + for (i = 1; i <= NUM_ITERATIONS; i++) { + TH_LOG("add digest list (%d/%d)...", i, NUM_ITERATIONS); + + digest_lists[0] = digest_list_generate_random(); + ASSERT_NE(NULL, digest_lists[0]) { + TH_LOG("digest_list_generate() failed"); + } + + ret = digest_list_upload(digest_lists[0], DIGEST_LIST_ADD, + UPLOAD_FILE, -1); + if (!ret) { + ret = digest_list_check(digest_lists[0], + DIGEST_LIST_ADD); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_check() failed"); + } + + ret = digest_list_upload(digest_lists[0], + DIGEST_LIST_DEL, UPLOAD_FILE, + -1); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_upload() failed"); + } + + ret = digest_list_check(digest_lists[0], + DIGEST_LIST_DEL); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_check() failed"); + } + } + + free(digest_lists[0]->buf); + free(digest_lists[0]); + digest_lists[0] = NULL; + } + + ret = read_buffer(DIGESTS_COUNT, &digests_count_after_ptr, &len, false, + true); + ASSERT_EQ(0, ret) { + TH_LOG("read_buffer() failed"); + } + + ASSERT_STREQ(digests_count_before, digests_count_after); +} + +#define IMA_MEASURE_RULES "dont_measure func=CRITICAL_DATA label=diglim euid=1000 \nmeasure func=CRITICAL_DATA label=diglim \n" + +/* This must be the last group of tests, otherwise an OOM will likely occur. */ +FIXTURE(test_measure) +{ +}; + +FIXTURE_SETUP(test_measure) +{ + char *cur_ima_policy = NULL; + size_t cur_ima_policy_len = 0; + bool rule_found = false; + int ret; + + ret = read_buffer(IMA_POLICY_PATH, &cur_ima_policy, &cur_ima_policy_len, + true, true); + ASSERT_EQ(0, ret) { + TH_LOG("read_buffer() failed"); + } + + rule_found = (strstr(cur_ima_policy, IMA_MEASURE_RULES) != NULL); + free(cur_ima_policy); + + if (!rule_found) { + ret = write_buffer(IMA_POLICY_PATH, IMA_MEASURE_RULES, + sizeof(IMA_MEASURE_RULES), -1); + ASSERT_EQ(0, ret) { + TH_LOG("write_buffer() failed"); + } + } +} + +FIXTURE_TEARDOWN(test_measure) +{ +} + +TEST_F_TIMEOUT(test_measure, digest_list_add_del_test_file_upload_measured, + UINT_MAX) +{ + int ret; + + digest_lists[0] = digest_list_generate(); + ASSERT_NE(NULL, digest_lists[0]) { + TH_LOG("digest_list_generate() failed"); + } + + digest_lists[0]->actions |= (1 << COMPACT_ACTION_IMA_MEASURED); + + ret = digest_list_upload(digest_lists[0], DIGEST_LIST_ADD, UPLOAD_FILE, + -1); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_upload() failed"); + } + + ret = digest_list_check(digest_lists[0], DIGEST_LIST_ADD); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_check() failed"); + } + + ret = digest_list_upload(digest_lists[0], DIGEST_LIST_DEL, UPLOAD_FILE, + -1); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_upload() failed"); + } + + ret = digest_list_check(digest_lists[0], DIGEST_LIST_DEL); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_check() failed"); + } + + free(digest_lists[0]->buf); + free(digest_lists[0]); + digest_lists[0] = NULL; +} + +void digest_list_check_measurement_list_test_common( + struct __test_metadata *_metadata, + enum upload_types upload_type) +{ + char *measurement_list = NULL; + size_t measurement_list_len; + char event_digest_name[512]; + bool entry_found; + int ret; + + digest_lists[0] = digest_list_generate(); + ASSERT_NE(NULL, digest_lists[0]) { + TH_LOG("digest_list_generate() failed"); + } + + digest_lists[0]->actions |= (1 << COMPACT_ACTION_IMA_MEASURED); + + ret = digest_list_upload(digest_lists[0], DIGEST_LIST_ADD, upload_type, + -1); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_upload() failed"); + } + + ret = digest_list_check(digest_lists[0], DIGEST_LIST_ADD); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_check() failed"); + } + + ret = read_buffer(IMA_MEASUREMENTS_PATH, &measurement_list, + &measurement_list_len, true, true); + ASSERT_EQ(0, ret) { + TH_LOG("read_buffer() failed"); + } + + snprintf(event_digest_name, sizeof(event_digest_name), + "%s:%s add_%s_digest_list.%s", + hash_algo_name[digest_lists[0]->algo], + digest_lists[0]->digest_str, + upload_type == UPLOAD_FILE ? "file" : "buffer", + digest_lists[0]->filename_suffix); + + entry_found = (strstr(measurement_list, event_digest_name) != NULL); + free(measurement_list); + + ASSERT_EQ(true, entry_found) { + TH_LOG("digest list not found in measurement list"); + } + + ret = digest_list_upload(digest_lists[0], DIGEST_LIST_DEL, upload_type, + 1000); + ASSERT_NE(0, ret) { + TH_LOG("digest_list_upload() success unexpected"); + } + + ret = digest_list_upload(digest_lists[0], DIGEST_LIST_DEL, upload_type, + -1); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_upload() failed"); + } + + ret = digest_list_check(digest_lists[0], DIGEST_LIST_DEL); + ASSERT_EQ(0, ret) { + TH_LOG("digest_list_check() failed"); + } + + measurement_list = NULL; + + ret = read_buffer(IMA_MEASUREMENTS_PATH, &measurement_list, + &measurement_list_len, true, true); + ASSERT_EQ(0, ret) { + TH_LOG("read_buffer() failed"); + } + + snprintf(event_digest_name, sizeof(event_digest_name), + "%s:%s del_%s_digest_list.%s", + hash_algo_name[digest_lists[0]->algo], + digest_lists[0]->digest_str, + upload_type == UPLOAD_FILE ? "file" : "buffer", + digest_lists[0]->filename_suffix); + + entry_found = (strstr(measurement_list, event_digest_name) != NULL); + free(measurement_list); + + ASSERT_EQ(true, entry_found) { + TH_LOG("digest list not found in measurement list"); + } + + free(digest_lists[0]->buf); + free(digest_lists[0]); + digest_lists[0] = NULL; +} + +TEST_F_TIMEOUT(test_measure, + digest_list_check_measurement_list_test_file_upload, UINT_MAX) +{ + digest_list_check_measurement_list_test_common(_metadata, UPLOAD_FILE); +} + +TEST_F_TIMEOUT(test_measure, + digest_list_check_measurement_list_test_buffer_upload, UINT_MAX) +{ + digest_list_check_measurement_list_test_common(_metadata, + UPLOAD_BUFFER); +} + +TEST_HARNESS_MAIN