On Wed, 2024-01-24 at 20:32 +0100, Enrico Bravi wrote:
> The template hash showed by the ascii_runtime_measurements and
> binary_runtime_measurements is the one calculated using sha1 and there is no
> possibility to change this value, despite the fact that the template hash is
> calculated using the hash algorithms corresponding to all the PCR banks
> configured in the TPM.
Hi Enrico
the missing part is that we should not modify existing files, to avoid
breaking existing applications.
> Add the support to retrieve the ima log with the template data hash calculated
> with a specific hash algorithm.
> Add a new file in the securityfs ima directory for each hash algo configured
> for the PCR banks of the TPM. Each new file has the name with the following
> structure:
>
> {binary, ascii}_runtime_measurements_<hash_algo_name>
>
> The <hash_algo_name> is used to select the template data hash algorithm to
> show
> in ima_ascii_measurements_show() and in ima_measurements_show().
> Legacy files are kept but as sysmbolic links which point to
Typo. Please use codespell on the patch.
> {binary, ascii}_runtime_measurements_sha1 files. These two files are created
> even if a TPM chip is not detected.
>
> As example, in the case a TPM chip is present and sha1 and sha256 are the
> configured PCR banks, the listing of the security/ima directory is the
> following:
>
> lr--r--r-- [...] ascii_runtime_measurements -> ascii_runtime_measurements_sha1
> -r--r----- [...] ascii_runtime_measurements_sha1
> -r--r----- [...] ascii_runtime_measurements_sha256
> lr--r--r-- [...] binary_runtime_measurements ->
> binary_runtime_measurements_sha1
> -r--r----- [...] binary_runtime_measurements_sha1
> -r--r----- [...] binary_runtime_measurements_sha256
> --w------- [...] policy
> -r--r----- [...] runtime_measurements_count
> -r--r----- [...] violations
Ok, great.
> Signed-off-by: Enrico Bravi <[email protected]>
> Signed-off-by: Silvia Sisinni <[email protected]>
>
> ---
>
> v3:
> - Added create_measurements_list_files function for measurements files
> creation.
> - Parametrized the remove_measurements_list_files function and add NULL
> check before freeing files' list.
> - Removed algorithm selection based on file name during ima_measurements_show
> and ima_ascii_mesurements_show, and selecting it comparing dentry address.
> - Allocate also sha1 file following the schema
> {binary, ascii}_runtime_measurements_<hash_algo_name> and keep legacy
> files as symbolic links to those files.
> - Allocate measurements files lists even if a TPM chip is not detected,
> adding only sha1 files.
>
> v2:
> - Changed the behaviour of configuring at boot time the template data hash
> algorithm.
> - Removed template data hash algo name prefix.
> - Removed ima_template_hash command line option.
> - Introducing a new file in the securityfs ima subdir for each PCR banks
> algorithm configured in the TPM.
> (suggested by Roberto)
>
> security/integrity/ima/ima_fs.c | 145 +++++++++++++++++++++++++++++---
> 1 file changed, 131 insertions(+), 14 deletions(-)
>
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index cd1683dad3bf..fb65ba9426a1 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -116,9 +116,12 @@ void ima_putc(struct seq_file *m, void *data, int
> datalen)
> seq_putc(m, *(char *)data++);
> }
>
> +static struct dentry **ima_ascii_measurements_files;
> +static struct dentry **ima_binary_measurements_files;
> +
> /* print format:
> * 32bit-le=pcr#
> - * char[20]=template digest
> + * char[n]=template digest
> * 32bit-le=template name size
> * char[n]=template name
> * [eventdata length]
> @@ -130,9 +133,25 @@ int ima_measurements_show(struct seq_file *m, void *v)
> struct ima_queue_entry *qe = v;
> struct ima_template_entry *e;
> char *template_name;
> + struct dentry *dentry;
> u32 pcr, namelen, template_data_len; /* temporary fields */
> bool is_ima_template = false;
> - int i;
> + int i, algo_idx;
> + enum hash_algo algo;
> +
> + dentry = m->file->f_path.dentry;
I like more file_dentry(m->file).
> + algo_idx = ima_sha1_idx;
> + algo = HASH_ALGO_SHA1;
> +
> + if (ima_tpm_chip) {
> + for (i = 0; i < NR_BANKS(ima_tpm_chip); i++) {
> + if (dentry == ima_binary_measurements_files[i]) {
> + algo_idx = i;
> + algo =
> ima_tpm_chip->allocated_banks[i].crypto_id;
> + break;
> + }
> + }
> + }
>
> /* get entry */
> e = qe->entry;
> @@ -151,7 +170,7 @@ int ima_measurements_show(struct seq_file *m, void *v)
> ima_putc(m, &pcr, sizeof(e->pcr));
>
> /* 2nd: template digest */
> - ima_putc(m, e->digests[ima_sha1_idx].digest, TPM_DIGEST_SIZE);
> + ima_putc(m, e->digests[algo_idx].digest, hash_digest_size[algo]);
>
> /* 3rd: template name size */
> namelen = !ima_canonical_fmt ? strlen(template_name) :
> @@ -220,7 +239,23 @@ static int ima_ascii_measurements_show(struct seq_file
> *m, void *v)
> struct ima_queue_entry *qe = v;
> struct ima_template_entry *e;
> char *template_name;
> - int i;
> + struct dentry *dentry;
> + int i, algo_idx;
> + enum hash_algo algo;
> +
> + dentry = m->file->f_path.dentry;
Same.
> + algo_idx = ima_sha1_idx;
> + algo = HASH_ALGO_SHA1;
> +
> + if (ima_tpm_chip) {
> + for (i = 0; i < NR_BANKS(ima_tpm_chip); i++) {
> + if (dentry == ima_ascii_measurements_files[i]) {
Uhm, why not iterating over ima_ascii_measurements_files?
But I have another comment on this.
Have a look at ima_crypto.c:
if (ima_sha1_idx < 0) {
ima_sha1_idx = NR_BANKS(ima_tpm_chip) + ima_extra_slots++;
if (ima_hash_algo == HASH_ALGO_SHA1)
ima_hash_algo_idx = ima_sha1_idx;
}
This is done because not necessarily the TPM has a SHA1 bank.
ima_extra_slots is already exported, you could use that to determine if
you need more slots than NR_BANKS(ima_tpm_chip).
> + algo_idx = i;
> + algo =
> ima_tpm_chip->allocated_banks[i].crypto_id;
> + break;
> + }
> + }
> + }
>
> /* get entry */
> e = qe->entry;
> @@ -233,8 +268,8 @@ static int ima_ascii_measurements_show(struct seq_file
> *m, void *v)
> /* 1st: PCR used (config option) */
> seq_printf(m, "%2d ", e->pcr);
>
> - /* 2nd: SHA1 template hash */
> - ima_print_digest(m, e->digests[ima_sha1_idx].digest, TPM_DIGEST_SIZE);
> + /* 2nd: template hash */
> + ima_print_digest(m, e->digests[algo_idx].digest,
> hash_digest_size[algo]);
>
> /* 3th: template name */
> seq_printf(m, " %s", template_name);
> @@ -379,6 +414,84 @@ static const struct seq_operations ima_policy_seqops = {
> };
> #endif
>
> +/*
> + * Remove the securityfs files created for each hash algo configured
> + * in the TPM, excluded ascii_runtime_measurements and
> + * binary_runtime_measurements.
> + */
It does not hurt following the kernel-doc format.
> +static void remove_measurements_list_files(struct dentry **files)
> +{
> + int i, len;
> + len = ima_tpm_chip ? NR_BANKS(ima_tpm_chip) : 1;
It is much better having a global variable with the number of array
elements.
> +
> + if (files)
> + {
This bracket should be one line up.
> + for (i = 0; i < len; i++) {
> + if (files[i]) {
> + securityfs_remove(files[i]);
> + }
> + }
> +
> + kfree(files);
> + }
> +}
> +
> +/*
> + * Allocate a file in the securityfs for each hash algo configured
> + * in the TPM (for ascii and binary output). In case no TPM chip is
> + * detected only sha1 files are created.
> + */
> +static int create_measurements_list_files(void)
> +{
> + int i, len;
> + u16 algo;
> + char file_name[NAME_MAX+1];
> + struct dentry *dfile;
> +
> + /*
> + * Allocate NR_BANKS(ima_tpm_chip) files in case a tpm chip is detected,
> + * otherwise allocate just one file for sha1.
> + */
> + len = ima_tpm_chip ? NR_BANKS(ima_tpm_chip) : 1;
See the comment above regarding ima_extra_slots.
If you export len (static), you can always use that without extra
logic.
> +
> + ima_ascii_measurements_files = kmalloc_array(len,
> + sizeof(struct dentry *), GFP_KERNEL);
> + if(!ima_ascii_measurements_files)
> + return -ENOMEM;
> +
> + ima_binary_measurements_files = kmalloc_array(len,
> + sizeof(struct dentry *), GFP_KERNEL);
> + if(!ima_binary_measurements_files)
> + return -ENOMEM;
> +
> + for (i = 0; i < len; i++) {
> + algo = ima_tpm_chip ?
> ima_tpm_chip->allocated_banks[i].crypto_id :
> + HASH_ALGO_SHA1;
I'm starting to think that maybe we should export ima_algo_array
instead, and follow that.
> +
> + sprintf(file_name, "ascii_runtime_measurements_%s",
> + hash_algo_name[algo]);
> + dfile = securityfs_create_file(file_name,
> + S_IRUSR | S_IRGRP, ima_dir, NULL,
> + &ima_ascii_measurements_ops);
> + if (IS_ERR(dfile))
> + return PTR_ERR(dfile);
> +
> + ima_ascii_measurements_files[i] = dfile;
> +
> + sprintf(file_name, "binary_runtime_measurements_%s",
> + hash_algo_name[algo]);
> + dfile = securityfs_create_file(file_name,
> + S_IRUSR | S_IRGRP, ima_dir, NULL,
> + &ima_measurements_ops);
> + if (IS_ERR(dfile))
> + return PTR_ERR(dfile);
> +
> + ima_binary_measurements_files[i] = dfile;
> + }
> +
> + return 0;
> +}
> +
> /*
> * ima_open_policy: sequentialize access to the policy file
> */
> @@ -465,19 +578,21 @@ int __init ima_fs_init(void)
> goto out;
> }
>
> - binary_runtime_measurements =
> - securityfs_create_file("binary_runtime_measurements",
> - S_IRUSR | S_IRGRP, ima_dir, NULL,
> - &ima_measurements_ops);
> + ret = create_measurements_list_files();
> + if (ret != 0)
> + goto out;
> +
> + binary_runtime_measurements = securityfs_create_symlink(
> + "binary_runtime_measurements", ima_dir,
> + "binary_runtime_measurements_sha1", NULL);
> if (IS_ERR(binary_runtime_measurements)) {
> ret = PTR_ERR(binary_runtime_measurements);
> goto out;
> }
>
> - ascii_runtime_measurements =
> - securityfs_create_file("ascii_runtime_measurements",
> - S_IRUSR | S_IRGRP, ima_dir, NULL,
> - &ima_ascii_measurements_ops);
> + ascii_runtime_measurements = securityfs_create_symlink(
> + "ascii_runtime_measurements", ima_dir,
> + "ascii_runtime_measurements_sha1", NULL);
> if (IS_ERR(ascii_runtime_measurements)) {
> ret = PTR_ERR(ascii_runtime_measurements);
> goto out;
> @@ -515,6 +630,8 @@ int __init ima_fs_init(void)
> securityfs_remove(runtime_measurements_count);
> securityfs_remove(ascii_runtime_measurements);
> securityfs_remove(binary_runtime_measurements);
> + remove_measurements_list_files(ima_ascii_measurements_files);
> + remove_measurements_list_files(ima_binary_measurements_files);
The rest is ok.
Thanks
Roberto
> securityfs_remove(ima_symlink);
> securityfs_remove(ima_dir);
>
> base-commit: 88035e5694a86a7167d490bb95e9df97a9bb162b