* Re: Are setuid shell scripts safe? (Implied by security_bprm_creds_for_exec)
From: Jan Kara @ 2025-12-09 12:28 UTC (permalink / raw)
To: Bernd Edlinger
Cc: Al Viro, Eric W. Biederman, Roberto Sassu, Alexey Dobriyan,
Oleg Nesterov, Kees Cook, Andy Lutomirski, Will Drewry,
Christian Brauner, Andrew Morton, Michal Hocko, Serge Hallyn,
James Morris, Randy Dunlap, Suren Baghdasaryan, Yafang Shao,
Helge Deller, Adrian Reber, Thomas Gleixner, Jens Axboe,
Alexei Starovoitov, linux-fsdevel@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-kselftest, linux-mm,
linux-security-module, tiozhang, Luis Chamberlain,
Paulo Alcantara (SUSE), Sergey Senozhatsky, Frederic Weisbecker,
YueHaibing, Paul Moore, Aleksa Sarai, Stefan Roesch, Chao Yu,
xu xin, Jeff Layton, Jan Kara, David Hildenbrand, Dave Chinner,
Shuah Khan, Elena Reshetova, David Windsor, Mateusz Guzik,
Ard Biesheuvel, Joel Fernandes (Google), Matthew Wilcox (Oracle),
Hans Liljestrand, Penglei Jiang, Lorenzo Stoakes, Adrian Ratiu,
Ingo Molnar, Peter Zijlstra (Intel), Cyrill Gorcunov,
Eric Dumazet, zohar, linux-integrity, Ryan Lee, apparmor
In-Reply-To: <GV2PPF74270EBEE0AAAE2EB22B668EE21A7E4A6A@GV2PPF74270EBEE.EURP195.PROD.OUTLOOK.COM>
On Thu 04-12-25 14:03:27, Bernd Edlinger wrote:
> On 12/4/25 06:49, Al Viro wrote:
> > On Wed, Dec 03, 2025 at 02:16:29PM +0100, Bernd Edlinger wrote:
> >
> >> Hmm, yes, that looks like an issue.
> >>
> >> I would have expected the security engine to look at bprm->filenanme
> >> especially in the case, when bprm->interp != bprm->filename,
> >> and check that it is not a sym-link with write-access for the
> >> current user and of course also that the bprm->file is not a regular file
> >> which is writable by the current user, if that is the case I would have expected
> >> the secuity engine to enforce non-new-privs on a SUID executable somehow.
> >
> > Check that _what_ is not a symlink? And while we are at it, what do write
> > permissions to any symlinks have to do with anything whatsoever?
>
> When we execve a normal executable, we do open the binary file with
> deny_write_access so this might allow the security engine to inspaect the
> binary, before it is used.
That would be seriously flawed IMO because there are lot of cases where
code is executed without deny_write_access() - like shared libraries, code
loaded by interpreter, and probably more.
> However this behavior has changed recently,
> now it has some exceptions, where even this behavior is no longer
> guaranteed for binary executables, due to commit
> 0357ef03c94ef835bd44a0658b8edb672a9dbf51, but why? I have no idea...
Because for hierarchical storage implementation you may need to fill in the
executable data from remote storage on demand and the deny_write_access
logic was making this impossible. We even tried to completely remove the
deny_write_access logic exactly because it has very limited use and
complicates things (commit 2a010c412853 ("fs: don't block i_writecount
during exec")) but that had to be reverted because some userspace depends
on the ETXTBUSY behavior.
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply
* [RFC][PATCH] ima: Add support for staging measurements for deletion
From: Roberto Sassu @ 2025-12-09 10:17 UTC (permalink / raw)
To: corbet, zohar, dmitry.kasatkin, eric.snowberg, paul, jmorris,
serge
Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
gregorylumen, chenste, nramas, Roberto Sassu
From: Roberto Sassu <roberto.sassu@huawei.com>
Introduce the ability of staging the entire of the IMA measurement list, or
a portion, for deletion. Staging means moving the current content of the
measurement list to a separate location, and allowing users to read and
delete it. This causes the measurement list to be atomically truncated
before new measurements can be added. Staging can be done only once at a
time.
User space is responsible to concatenate the staged IMA measurements list
portions following the temporal order in which the operations were done,
together with the current measurement list. Then, it can send the collected
data to the remote verifiers.
The benefit of this solution is the ability to free precious kernel memory,
in exchange of delegating user space to reconstruct the full measurement
list from the chunks. No trust needs to be given to user space, since the
integrity of the measurement list is protected by the TPM.
By default, staging the measurements list for deletion does not alter the
hash table. When staging is done, IMA is still able to detect collisions on
the staged and later deleted measurement entries, by keeping the entry
digests (only template data are freed).
However, since during the measurements list serialization only the SHA1
digest is passed, and since there are no template data to recalculate the
other digests from, the hash table is currently not populated with digests
from staged/deleted entries after kexec().
Introduce the new kernel option ima_flush_htable to decide whether or not
the digests of staged measurement entries are flushed from the hash table.
Then, introduce ascii_runtime_measurements_staged_<algo> and
binary_runtime_measurement_staged_<algo> interfaces to stage/delete the
measurements. Use 'echo A > <IMA interface>' and 'echo D > <IMA interface>'
to respectively stage and delete the entire measurements list. Use
'echo N > <IMA interface>', with N between 1 and ULONG_MAX, to stage the
selected portion of the measurements list.
The ima_measure_users counter (protected by the ima_measure_lock mutex) has
been introduced to protect access to the measurement list and the staged
part. The open method of all the measurement interfaces has been extended
to allow only one writer at a time or, in alternative, multiple readers.
The write permission is used to stage/delete the measurements, the read
permission to read them. Write requires also the CAP_SYS_ADMIN capability.
Finally, introduce the _notrim version of the run-time measurements count
and the binary measurements list size, to display them in the kexec-related
critical data records.
Note: This code derives from the Alt-IMA Huawei project, and is being
released under the dual license model (GPL-2.0 OR MIT).
Link: https://github.com/linux-integrity/linux/issues/1
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
.../admin-guide/kernel-parameters.txt | 4 +
security/integrity/ima/ima.h | 10 +-
security/integrity/ima/ima_fs.c | 222 +++++++++++++++++-
security/integrity/ima/ima_kexec.c | 13 +-
security/integrity/ima/ima_queue.c | 111 ++++++++-
5 files changed, 340 insertions(+), 20 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 6c42061ca20e..355d8930e3ac 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -2215,6 +2215,10 @@
Use the canonical format for the binary runtime
measurements, instead of host native format.
+ ima_flush_htable [IMA]
+ Flush the measurement list hash table when staging all
+ or a part of it for deletion.
+
ima_hash= [IMA]
Format: { md5 | sha1 | rmd160 | sha256 | sha384
| sha512 | ... }
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index e3d71d8d56e3..d7aa4a0f79b1 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -117,6 +117,8 @@ struct ima_queue_entry {
struct ima_template_entry *entry;
};
extern struct list_head ima_measurements; /* list of all measurements */
+extern struct list_head ima_measurements_staged; /* list of staged meas. */
+extern bool ima_measurements_staged_exist; /* If there are staged meas. */
/* Some details preceding the binary serialized measurement list */
struct ima_kexec_hdr {
@@ -281,10 +283,12 @@ struct ima_template_desc *ima_template_desc_current(void);
struct ima_template_desc *ima_template_desc_buf(void);
struct ima_template_desc *lookup_template_desc(const char *name);
bool ima_template_has_modsig(const struct ima_template_desc *ima_template);
+int ima_queue_stage(unsigned long req_value);
+int ima_queue_delete_staged(void);
int ima_restore_measurement_entry(struct ima_template_entry *entry);
int ima_restore_measurement_list(loff_t bufsize, void *buf);
int ima_measurements_show(struct seq_file *m, void *v);
-unsigned long ima_get_binary_runtime_size(void);
+unsigned long ima_get_binary_runtime_size(bool notrim);
int ima_init_template(void);
void ima_init_template_list(void);
int __init ima_init_digests(void);
@@ -298,11 +302,13 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
extern spinlock_t ima_queue_lock;
struct ima_h_table {
- atomic_long_t len; /* number of stored measurements in the list */
+ atomic_long_t len; /* current num of stored meas. in the list */
+ atomic_long_t len_notrim; /* total num of stored meas. in the list */
atomic_long_t violations;
struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
};
extern struct ima_h_table ima_htable;
+extern struct mutex ima_extend_list_mutex;
static inline unsigned int ima_hash_key(u8 *digest)
{
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 87045b09f120..321c98ae0e55 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -24,7 +24,12 @@
#include "ima.h"
+/* Requests: ('A', [1, ULONG_MAX])\n (stage all/N) or D\n (delete staged) */
+#define STAGED_REQ_LENGTH 21
+
static DEFINE_MUTEX(ima_write_mutex);
+static DEFINE_MUTEX(ima_measure_lock);
+static long ima_measure_users;
bool ima_canonical_fmt;
static int __init default_canonical_fmt_setup(char *str)
@@ -74,14 +79,15 @@ static const struct file_operations ima_measurements_count_ops = {
};
/* returns pointer to hlist_node */
-static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
+static void *_ima_measurements_start(struct seq_file *m, loff_t *pos,
+ struct list_head *head)
{
loff_t l = *pos;
struct ima_queue_entry *qe;
/* we need a lock since pos could point beyond last element */
rcu_read_lock();
- list_for_each_entry_rcu(qe, &ima_measurements, later) {
+ list_for_each_entry_rcu(qe, head, later) {
if (!l--) {
rcu_read_unlock();
return qe;
@@ -91,7 +97,18 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
return NULL;
}
-static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
+static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
+{
+ return _ima_measurements_start(m, pos, &ima_measurements);
+}
+
+static void *ima_measurements_staged_start(struct seq_file *m, loff_t *pos)
+{
+ return _ima_measurements_start(m, pos, &ima_measurements_staged);
+}
+
+static void *_ima_measurements_next(struct seq_file *m, void *v, loff_t *pos,
+ struct list_head *head)
{
struct ima_queue_entry *qe = v;
@@ -103,7 +120,18 @@ static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
rcu_read_unlock();
(*pos)++;
- return (&qe->later == &ima_measurements) ? NULL : qe;
+ return (&qe->later == head) ? NULL : qe;
+}
+
+static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ return _ima_measurements_next(m, v, pos, &ima_measurements);
+}
+
+static void *ima_measurements_staged_next(struct seq_file *m, void *v,
+ loff_t *pos)
+{
+ return _ima_measurements_next(m, v, pos, &ima_measurements_staged);
}
static void ima_measurements_stop(struct seq_file *m, void *v)
@@ -202,16 +230,138 @@ static const struct seq_operations ima_measurments_seqops = {
.show = ima_measurements_show
};
+static int _ima_measurements_open(struct inode *inode, struct file *file,
+ const struct seq_operations *seq_ops)
+{
+ bool write = !!(file->f_mode & FMODE_WRITE);
+ int ret;
+
+ if (write && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ mutex_lock(&ima_measure_lock);
+ if ((write && ima_measure_users != 0) ||
+ (!write && ima_measure_users < 0)) {
+ mutex_unlock(&ima_measure_lock);
+ return -EBUSY;
+ }
+
+ ret = seq_open(file, seq_ops);
+ if (ret < 0) {
+ mutex_unlock(&ima_measure_lock);
+ return ret;
+ }
+
+ if (write)
+ ima_measure_users--;
+ else
+ ima_measure_users++;
+
+ mutex_unlock(&ima_measure_lock);
+ return ret;
+}
+
static int ima_measurements_open(struct inode *inode, struct file *file)
{
- return seq_open(file, &ima_measurments_seqops);
+ return _ima_measurements_open(inode, file, &ima_measurments_seqops);
+}
+
+static int ima_measurements_release(struct inode *inode, struct file *file)
+{
+ bool write = !!(file->f_mode & FMODE_WRITE);
+ int ret;
+
+ mutex_lock(&ima_measure_lock);
+ ret = seq_release(inode, file);
+ if (!ret) {
+ if (write)
+ ima_measure_users++;
+ else
+ ima_measure_users--;
+ }
+
+ mutex_unlock(&ima_measure_lock);
+ return ret;
}
static const struct file_operations ima_measurements_ops = {
.open = ima_measurements_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release,
+ .release = ima_measurements_release,
+};
+
+static const struct seq_operations ima_measurments_staged_seqops = {
+ .start = ima_measurements_staged_start,
+ .next = ima_measurements_staged_next,
+ .stop = ima_measurements_stop,
+ .show = ima_measurements_show
+};
+
+static int ima_measurements_staged_open(struct inode *inode, struct file *file)
+{
+ return _ima_measurements_open(inode, file,
+ &ima_measurments_staged_seqops);
+}
+
+static ssize_t ima_measurements_staged_read(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ if (!ima_measurements_staged_exist)
+ return -ENOENT;
+
+ return seq_read(file, buf, size, ppos);
+}
+
+static ssize_t ima_measurements_staged_write(struct file *file,
+ const char __user *buf,
+ size_t datalen, loff_t *ppos)
+{
+ char req[STAGED_REQ_LENGTH], *req_ptr = req;
+ unsigned long req_value;
+ int ret;
+
+ if (*ppos > 0 || datalen < 2 || datalen > STAGED_REQ_LENGTH)
+ return -EINVAL;
+
+ ret = copy_from_user(req, buf, datalen);
+ if (ret < 0)
+ return ret;
+
+ if (strsep(&req_ptr, "\n") == NULL)
+ return -EINVAL;
+
+ switch (req[0]) {
+ case 'A':
+ if (datalen != 2 || req[1] != '\0')
+ return -EINVAL;
+
+ ret = ima_queue_stage(ULONG_MAX);
+ break;
+ case 'D':
+ if (datalen != 2 || req[1] != '\0')
+ return -EINVAL;
+
+ ret = ima_queue_delete_staged();
+ break;
+ default:
+ ret = kstrtoul(req, 0, &req_value);
+ if (!ret)
+ ret = ima_queue_stage(req_value);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return datalen;
+}
+
+static const struct file_operations ima_measurements_staged_ops = {
+ .open = ima_measurements_staged_open,
+ .read = ima_measurements_staged_read,
+ .write = ima_measurements_staged_write,
+ .llseek = seq_lseek,
+ .release = ima_measurements_release,
};
void ima_print_digest(struct seq_file *m, u8 *digest, u32 size)
@@ -279,14 +429,37 @@ static const struct seq_operations ima_ascii_measurements_seqops = {
static int ima_ascii_measurements_open(struct inode *inode, struct file *file)
{
- return seq_open(file, &ima_ascii_measurements_seqops);
+ return _ima_measurements_open(inode, file,
+ &ima_ascii_measurements_seqops);
}
static const struct file_operations ima_ascii_measurements_ops = {
.open = ima_ascii_measurements_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release,
+ .release = ima_measurements_release,
+};
+
+static const struct seq_operations ima_ascii_measurements_staged_seqops = {
+ .start = ima_measurements_staged_start,
+ .next = ima_measurements_staged_next,
+ .stop = ima_measurements_stop,
+ .show = ima_ascii_measurements_show
+};
+
+static int ima_ascii_measurements_staged_open(struct inode *inode,
+ struct file *file)
+{
+ return _ima_measurements_open(inode, file,
+ &ima_ascii_measurements_staged_seqops);
+}
+
+static const struct file_operations ima_ascii_measurements_staged_ops = {
+ .open = ima_ascii_measurements_staged_open,
+ .read = ima_measurements_staged_read,
+ .write = ima_measurements_staged_write,
+ .llseek = seq_lseek,
+ .release = ima_measurements_release,
};
static ssize_t ima_read_policy(char *path)
@@ -419,6 +592,25 @@ static int __init create_securityfs_measurement_lists(void)
&ima_measurements_ops);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
+
+ sprintf(file_name, "ascii_runtime_measurements_staged_%s",
+ hash_algo_name[algo]);
+ dentry = securityfs_create_file(file_name,
+ S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP,
+ ima_dir, (void *)(uintptr_t)i,
+ &ima_ascii_measurements_staged_ops);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ sprintf(file_name, "binary_runtime_measurements_staged_%s",
+ hash_algo_name[algo]);
+ dentry = securityfs_create_file(file_name,
+ S_IRUSR | S_IRGRP |
+ S_IWUSR | S_IWGRP,
+ ima_dir, (void *)(uintptr_t)i,
+ &ima_measurements_staged_ops);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
}
return 0;
@@ -528,6 +720,20 @@ int __init ima_fs_init(void)
goto out;
}
+ dentry = securityfs_create_symlink("binary_runtime_measurements_staged",
+ ima_dir, "binary_runtime_measurements_staged_sha1", NULL);
+ if (IS_ERR(dentry)) {
+ ret = PTR_ERR(dentry);
+ goto out;
+ }
+
+ dentry = securityfs_create_symlink("ascii_runtime_measurements_staged",
+ ima_dir, "ascii_runtime_measurements_staged_sha1", NULL);
+ if (IS_ERR(dentry)) {
+ ret = PTR_ERR(dentry);
+ goto out;
+ }
+
dentry = securityfs_create_file("runtime_measurements_count",
S_IRUSR | S_IRGRP, ima_dir, NULL,
&ima_measurements_count_ops);
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
index 7362f68f2d8b..23a20300da7b 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -40,8 +40,8 @@ void ima_measure_kexec_event(const char *event_name)
long len;
int n;
- buf_size = ima_get_binary_runtime_size();
- len = atomic_long_read(&ima_htable.len);
+ buf_size = ima_get_binary_runtime_size(true);
+ len = atomic_long_read(&ima_htable.len_notrim);
n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN,
"kexec_segment_size=%lu;ima_binary_runtime_size=%lu;"
@@ -93,8 +93,10 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
memset(&khdr, 0, sizeof(khdr));
khdr.version = 1;
- /* This is an append-only list, no need to hold the RCU read lock */
- list_for_each_entry_rcu(qe, &ima_measurements, later, true) {
+
+ /* It can race with ima_queue_stage(). */
+ mutex_lock(&ima_extend_list_mutex);
+ list_for_each_entry(qe, &ima_measurements, later) {
if (ima_kexec_file.count < ima_kexec_file.size) {
khdr.count++;
ima_measurements_show(&ima_kexec_file, qe);
@@ -103,6 +105,7 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
break;
}
}
+ mutex_unlock(&ima_extend_list_mutex);
/*
* fill in reserved space with some buffer details
@@ -157,7 +160,7 @@ void ima_add_kexec_buffer(struct kimage *image)
else
extra_memory = CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB * 1024;
- binary_runtime_size = ima_get_binary_runtime_size() + extra_memory;
+ binary_runtime_size = ima_get_binary_runtime_size(false) + extra_memory;
if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE)
kexec_segment_size = ULONG_MAX;
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 590637e81ad1..868f216ac343 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -22,19 +22,32 @@
#define AUDIT_CAUSE_LEN_MAX 32
+bool ima_flush_htable;
+static int __init ima_flush_htable_setup(char *str)
+{
+ ima_flush_htable = true;
+ return 1;
+}
+__setup("ima_flush_htable", ima_flush_htable_setup);
+
/* pre-allocated array of tpm_digest structures to extend a PCR */
static struct tpm_digest *digests;
LIST_HEAD(ima_measurements); /* list of all measurements */
+LIST_HEAD(ima_measurements_staged); /* list of staged measurements */
+bool ima_measurements_staged_exist; /* If there are staged measurements */
#ifdef CONFIG_IMA_KEXEC
static unsigned long binary_runtime_size;
+static unsigned long binary_runtime_size_notrim;
#else
static unsigned long binary_runtime_size = ULONG_MAX;
+static unsigned long binary_runtime_size_notrim = ULONG_MAX;
#endif
/* key: inode (before secure-hashing a file) */
struct ima_h_table ima_htable = {
.len = ATOMIC_LONG_INIT(0),
+ .len_notrim = ATOMIC_LONG_INIT(0),
.violations = ATOMIC_LONG_INIT(0),
.queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
};
@@ -43,7 +56,7 @@ struct ima_h_table ima_htable = {
* and extending the TPM PCR aggregate. Since tpm_extend can take
* long (and the tpm driver uses a mutex), we can't use the spinlock.
*/
-static DEFINE_MUTEX(ima_extend_list_mutex);
+DEFINE_MUTEX(ima_extend_list_mutex);
/*
* Used internally by the kernel to suspend measurements.
@@ -114,15 +127,19 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
list_add_tail_rcu(&qe->later, &ima_measurements);
atomic_long_inc(&ima_htable.len);
+ atomic_long_inc(&ima_htable.len_notrim);
if (update_htable) {
key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest);
hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);
}
- if (binary_runtime_size != ULONG_MAX) {
+ if (binary_runtime_size_notrim != ULONG_MAX) {
int size;
size = get_binary_runtime_size(entry);
+ binary_runtime_size_notrim =
+ (binary_runtime_size_notrim < ULONG_MAX - size) ?
+ binary_runtime_size_notrim + size : ULONG_MAX;
binary_runtime_size = (binary_runtime_size < ULONG_MAX - size) ?
binary_runtime_size + size : ULONG_MAX;
}
@@ -134,12 +151,18 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
* entire binary_runtime_measurement list, including the ima_kexec_hdr
* structure.
*/
-unsigned long ima_get_binary_runtime_size(void)
+unsigned long ima_get_binary_runtime_size(bool notrim)
{
- if (binary_runtime_size >= (ULONG_MAX - sizeof(struct ima_kexec_hdr)))
+ unsigned long *val;
+
+ mutex_lock(&ima_extend_list_mutex);
+ val = (notrim) ? &binary_runtime_size_notrim : &binary_runtime_size;
+ mutex_unlock(&ima_extend_list_mutex);
+
+ if (*val >= (ULONG_MAX - sizeof(struct ima_kexec_hdr)))
return ULONG_MAX;
else
- return binary_runtime_size + sizeof(struct ima_kexec_hdr);
+ return *val + sizeof(struct ima_kexec_hdr);
}
static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr)
@@ -220,6 +243,84 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
return result;
}
+int ima_queue_stage(unsigned long req_value)
+{
+ unsigned long req_value_copy = req_value, to_remove = 0;
+ struct ima_queue_entry *qe;
+
+ if (ima_measurements_staged_exist)
+ return -EEXIST;
+
+ mutex_lock(&ima_extend_list_mutex);
+ if (list_empty(&ima_measurements)) {
+ mutex_unlock(&ima_extend_list_mutex);
+ return -ENOENT;
+ }
+
+ if (req_value == ULONG_MAX) {
+ list_replace(&ima_measurements, &ima_measurements_staged);
+ INIT_LIST_HEAD(&ima_measurements);
+ atomic_long_set(&ima_htable.len, 0);
+ if (IS_ENABLED(CONFIG_IMA_KEXEC))
+ binary_runtime_size = 0;
+ } else {
+ list_for_each_entry(qe, &ima_measurements, later) {
+ to_remove += get_binary_runtime_size(qe->entry);
+ if (--req_value_copy == 0)
+ break;
+ }
+
+ if (req_value_copy > 0) {
+ mutex_unlock(&ima_extend_list_mutex);
+ return -ENOENT;
+ }
+
+ __list_cut_position(&ima_measurements_staged, &ima_measurements,
+ &qe->later);
+ atomic_long_sub(req_value, &ima_htable.len);
+ if (IS_ENABLED(CONFIG_IMA_KEXEC))
+ binary_runtime_size -= to_remove;
+ }
+
+ if (ima_flush_htable) {
+ list_for_each_entry(qe, &ima_measurements_staged, later)
+ /* It can race with ima_lookup_digest_entry(). */
+ hlist_del_rcu(&qe->hnext);
+ }
+
+ mutex_unlock(&ima_extend_list_mutex);
+ ima_measurements_staged_exist = true;
+ return 0;
+}
+
+int ima_queue_delete_staged(void)
+{
+ struct ima_queue_entry *qe, *qe_tmp;
+ unsigned int i;
+
+ if (!ima_measurements_staged_exist)
+ return -ENOENT;
+
+ list_for_each_entry_safe(qe, qe_tmp, &ima_measurements_staged, later) {
+ for (i = 0; i < qe->entry->template_desc->num_fields; i++) {
+ kfree(qe->entry->template_data[i].data);
+ qe->entry->template_data[i].data = NULL;
+ qe->entry->template_data[i].len = 0;
+ }
+
+ list_del(&qe->later);
+
+ if (ima_flush_htable) {
+ kfree(qe->entry->digests);
+ kfree(qe->entry);
+ kfree(qe);
+ }
+ }
+
+ ima_measurements_staged_exist = false;
+ return 0;
+}
+
int ima_restore_measurement_entry(struct ima_template_entry *entry)
{
int result = 0;
--
2.43.0
^ permalink raw reply related
* [PATCH v2 3/5] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-09 10:05 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, James Bottomley,
Mimi Zohar, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM
In-Reply-To: <20251209100529.1700779-1-jarkko@kernel.org>
tpm2_load_cmd() and tpm2_unseal_cmd() use the same parent, and calls to
tpm_buf_append_name() cause the exact same TPM2_ReadPublic command to be
sent to the chip, causing unnecessary traffic.
1. Export tpm2_read_public in order to make it callable from
'trusted_tpm2'.
2. Re-orchestrate tpm2_seal_trusted() and tpm2_unseal_trusted() in order to
halve the name resolutions required:
2a. Move tpm2_read_public() calls into trusted_tpm2.
2b. Pass TPM name to tpm_buf_append_name().
2c. Rework tpm_buf_append_name() to use the pre-resolved name.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
drivers/char/tpm/tpm2-cmd.c | 3 +-
drivers/char/tpm/tpm2-sessions.c | 95 +++++------------
include/linux/tpm.h | 10 +-
security/keys/trusted-keys/trusted_tpm2.c | 124 ++++++++++++++--------
4 files changed, 118 insertions(+), 114 deletions(-)
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 2682f5ec09cb..5b04e74b6377 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -199,7 +199,8 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
if (!disable_pcr_integrity) {
- rc = tpm_buf_append_name(chip, buf, pcr_idx, NULL);
+ rc = tpm_buf_append_name(chip, buf, pcr_idx, (u8 *)&pcr_idx,
+ sizeof(u32));
if (rc)
return rc;
tpm_buf_append_hmac_session(chip, buf, 0, NULL, 0);
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index a0c88fb1804c..0816a91134fc 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -136,8 +136,8 @@ struct tpm2_auth {
* handle, but they are part of the session by name, which
* we must compute and remember
*/
- u32 name_h[AUTH_MAX_NAMES];
u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
+ u16 name_size_tbl[AUTH_MAX_NAMES];
};
#ifdef CONFIG_TCG_TPM2_HMAC
@@ -163,7 +163,17 @@ static int name_size(const u8 *name)
}
}
-static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+/**
+ * tpm2_read_public: Resolve TPM name for a handle
+ * @chip: TPM chip to use.
+ * @handle: TPM handle.
+ * @name: A buffer for returning the name blob. Must have a
+ * capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
+ *
+ * Returns size of TPM handle name of success.
+ * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
+ */
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
{
u32 mso = tpm2_handle_mso(handle);
off_t offset = TPM_HEADER_SIZE;
@@ -212,14 +222,16 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
memcpy(name, &buf->data[offset], rc);
return name_size_alg;
}
+EXPORT_SYMBOL_GPL(tpm2_read_public);
#endif /* CONFIG_TCG_TPM2_HMAC */
/**
- * tpm_buf_append_name() - add a handle area to the buffer
- * @chip: the TPM chip structure
- * @buf: The buffer to be appended
- * @handle: The handle to be appended
- * @name: The name of the handle (may be NULL)
+ * tpm_buf_append_name() - Append a handle and store TPM name
+ * @chip: TPM chip to use.
+ * @buf: TPM buffer containing the TPM command in-transit.
+ * @handle: TPM handle to be appended.
+ * @name: TPM name of the handle
+ * @name_size: Size of the TPM name.
*
* In order to compute session HMACs, we need to know the names of the
* objects pointed to by the handles. For most objects, this is simply
@@ -236,15 +248,14 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
* will be caused by an incorrect programming model and indicated by a
* kernel message.
*
- * Ends the authorization session on failure.
+ * Returns zero on success.
+ * Returns -EIO when the authorization area state is malformed.
*/
int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name)
+ u32 handle, u8 *name, u16 name_size)
{
#ifdef CONFIG_TCG_TPM2_HMAC
- enum tpm2_mso_type mso = tpm2_handle_mso(handle);
struct tpm2_auth *auth;
- u16 name_size_alg;
int slot;
int ret;
#endif
@@ -269,36 +280,15 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
}
tpm_buf_append_u32(buf, handle);
auth->session += 4;
-
- if (mso == TPM2_MSO_PERSISTENT ||
- mso == TPM2_MSO_VOLATILE ||
- mso == TPM2_MSO_NVRAM) {
- if (!name) {
- ret = tpm2_read_public(chip, handle, auth->name[slot]);
- if (ret < 0)
- goto err;
-
- name_size_alg = ret;
- }
- } else {
- if (name) {
- dev_err(&chip->dev, "handle 0x%08x does not use a name\n",
- handle);
- ret = -EIO;
- goto err;
- }
- }
-
- auth->name_h[slot] = handle;
- if (name)
- memcpy(auth->name[slot], name, name_size_alg);
+ memcpy(auth->name[slot], name, name_size);
+ auth->name_size_tbl[slot] = name_size;
#endif
return 0;
#ifdef CONFIG_TCG_TPM2_HMAC
err:
tpm2_end_auth_session(chip);
- return tpm_ret_to_err(ret);
+ return ret;
#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -606,22 +596,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
attrs = chip->cc_attrs_tbl[i];
handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
+ offset_s += handles * sizeof(u32);
- /*
- * just check the names, it's easy to make mistakes. This
- * would happen if someone added a handle via
- * tpm_buf_append_u32() instead of tpm_buf_append_name()
- */
- for (i = 0; i < handles; i++) {
- u32 handle = tpm_buf_read_u32(buf, &offset_s);
-
- if (auth->name_h[i] != handle) {
- dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
- ret = -EIO;
- goto err;
- }
- }
- /* point offset_s to the start of the sessions */
val = tpm_buf_read_u32(buf, &offset_s);
/* point offset_p to the start of the parameters */
offset_p = offset_s + val;
@@ -682,23 +658,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
/* ordinal is already BE */
sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
/* add the handle names */
- for (i = 0; i < handles; i++) {
- enum tpm2_mso_type mso = tpm2_handle_mso(auth->name_h[i]);
-
- if (mso == TPM2_MSO_PERSISTENT ||
- mso == TPM2_MSO_VOLATILE ||
- mso == TPM2_MSO_NVRAM) {
- ret = name_size(auth->name[i]);
- if (ret < 0)
- goto err;
-
- sha256_update(&sctx, auth->name[i], ret);
- } else {
- __be32 h = cpu_to_be32(auth->name_h[i]);
-
- sha256_update(&sctx, (u8 *)&h, 4);
- }
- }
+ for (i = 0; i < handles; i++)
+ sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
if (offset_s != tpm_buf_length(buf))
sha256_update(&sctx, &buf->data[offset_s],
tpm_buf_length(buf) - offset_s);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 922a43ef23b5..9f684fc7ae04 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -543,7 +543,7 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
}
int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name);
+ u32 handle, u8 *name, u16 name_size);
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
u8 attributes, u8 *passphrase,
int passphraselen);
@@ -557,6 +557,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
int rc);
void tpm2_end_auth_session(struct tpm_chip *chip);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
#else
#include <linux/unaligned.h>
@@ -580,6 +581,13 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
{
return rc;
}
+
+static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle,
+ void *name)
+{
+ memcpy(name, &handle, sizeof(u32));
+ return sizeof(u32);
+}
#endif /* CONFIG_TCG_TPM2_HMAC */
#endif
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 27424e1a4a63..63539b344ffb 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -203,7 +203,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
+ u8 parent_name[TPM2_MAX_NAME_SIZE];
off_t offset = TPM_HEADER_SIZE;
+ u16 parent_name_size;
int blob_len = 0;
int hash;
u32 flags;
@@ -220,6 +222,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
if (rc)
return rc;
+ rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+ if (rc < 0)
+ goto out_put;
+
+ parent_name_size = rc;
+
rc = tpm2_start_auth_session(chip);
if (rc)
goto out_put;
@@ -234,7 +242,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
- rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+ parent_name_size);
if (rc)
goto out_put;
@@ -326,48 +335,38 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
}
/**
- * tpm2_load_cmd() - execute a TPM2_Load command
- *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: returned blob handle
+ * tpm2_load_cmd() - Execute TPM2_Load
+ * @chip: TPM chip to use.
+ * @payload: Key data in clear text.
+ * @options: Trusted key options.
+ * @parent_name: A cryptographic name, i.e. a TPMT_HA blob, of the
+ * parent key.
+ * @blob: The decoded payload for the key.
+ * @blob_handle: On success, will contain handle to the loaded keyedhash
+ * blob.
*
- * Return: 0 on success.
- * -E2BIG on wrong payload size.
- * -EPERM on tpm error status.
- * < 0 error from tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
*/
static int tpm2_load_cmd(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options,
+ u8 *parent_name,
+ u16 parent_name_size,
+ const u8 *blob,
u32 *blob_handle)
{
u8 *blob_ref __free(kfree) = NULL;
unsigned int private_len;
unsigned int public_len;
unsigned int blob_len;
- u8 *blob, *pub;
+ const u8 *pub;
int rc;
u32 attrs;
- rc = tpm2_key_decode(payload, options, &blob);
- if (rc) {
- /* old form */
- blob = payload->blob;
- payload->old_format = 1;
- } else {
- /* Bind for cleanup: */
- blob_ref = blob;
- }
-
- /* new format carries keyhandle but old format doesn't */
- if (!options->keyhandle)
- return -EINVAL;
-
/* must be big enough for at least the two be16 size counts */
if (payload->blob_len < 4)
- return -EINVAL;
+ return -E2BIG;
private_len = get_unaligned_be16(blob);
@@ -406,7 +405,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
- rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+ parent_name_size);
if (rc)
return rc;
@@ -434,20 +434,23 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
}
/**
- * tpm2_unseal_cmd() - execute a TPM2_Unload command
+ * tpm2_unseal_cmd() - Execute TPM2_Unload
*
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: blob handle
+ * @chip: TPM chip to use
+ * @payload: Key data in clear text.
+ * @options: Trusted key options.
+ * @parent_name: A cryptographic name, i.e. a TPMT_HA blob, of the
+ * parent key.
+ * @blob_handle: Handle to the loaded keyedhash blob.
*
- * Return: 0 on success
- * -EPERM on tpm error status
- * < 0 error from tpm_send
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
*/
static int tpm2_unseal_cmd(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options,
+ u8 *parent_name,
+ u16 parent_name_size,
u32 blob_handle)
{
u16 data_len;
@@ -465,7 +468,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
- rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+ parent_name_size);
if (rc)
return rc;
@@ -526,30 +530,60 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
}
/**
- * tpm2_unseal_trusted() - unseal the payload of a trusted key
+ * tpm2_unseal_trusted() - Unseal a trusted key
+ * @chip: TPM chip to use.
+ * @payload: Key data in clear text.
+ * @options: Trusted key options.
*
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- *
- * Return: Same as with tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Return -EINVAL when parent's key handle has not been set.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load or TPM2_Unseal
+ * fails.
*/
int tpm2_unseal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
+ u8 *blob_ref __free(kfree) = NULL;
+ u8 parent_name[TPM2_MAX_NAME_SIZE];
+ u16 parent_name_size;
u32 blob_handle;
+ u8 *blob;
int rc;
+ /*
+ * Try to decode the provided blob as an ASN.1 blob. Assume that the
+ * blob is in the legacy format if decoding does not end successfully.
+ */
+ rc = tpm2_key_decode(payload, options, &blob);
+ if (rc) {
+ blob = payload->blob;
+ payload->old_format = 1;
+ } else {
+ blob_ref = blob;
+ }
+
+ if (!options->keyhandle)
+ return -EINVAL;
+
rc = tpm_try_get_ops(chip);
if (rc)
return rc;
- rc = tpm2_load_cmd(chip, payload, options, &blob_handle);
+ rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+ if (rc < 0)
+ goto out;
+
+ parent_name_size = rc;
+
+ rc = tpm2_load_cmd(chip, payload, options, parent_name,
+ parent_name_size, blob, &blob_handle);
if (rc)
goto out;
- rc = tpm2_unseal_cmd(chip, payload, options, blob_handle);
+ rc = tpm2_unseal_cmd(chip, payload, options, parent_name,
+ parent_name_size, blob_handle);
+
tpm2_flush_context(chip, blob_handle);
out:
--
2.52.0
^ permalink raw reply related
* [PATCH v2 1/5] KEYS: trusted: Remove dead branch from tpm2_unseal_cmd
From: Jarkko Sakkinen @ 2025-12-09 10:05 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, James Bottomley,
Mimi Zohar, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM
In-Reply-To: <20251209100529.1700779-1-jarkko@kernel.org>
TPM2_Unseal requires TPM2_ST_SESSIONS, and tpm2_unseal_cmd() always does
set up either password or HMAC session.
Remove the branch in tpm2_unseal_cmd() conditionally setting
TPM2_ST_NO_SESSIONS. It is faulty but luckily it is never exercised at
run-time, and thus does not cause regressions.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
security/keys/trusted-keys/trusted_tpm2.c | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 9074ae1a5896..27424e1a4a63 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -450,9 +450,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
struct trusted_key_options *options,
u32 blob_handle)
{
- struct tpm_header *head;
u16 data_len;
- int offset;
u8 *data;
int rc;
@@ -488,14 +486,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
tpm_buf_append_u16(buf, options->blobauth_len);
tpm_buf_append(buf, options->blobauth, options->blobauth_len);
- if (tpm2_chip_auth(chip)) {
+ if (tpm2_chip_auth(chip))
tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT, NULL, 0);
- } else {
- offset = buf->handles * 4 + TPM_HEADER_SIZE;
- head = (struct tpm_header *)buf->data;
- if (tpm_buf_length(buf) == offset)
- head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
- }
}
rc = tpm_buf_fill_hmac_session(chip, buf);
--
2.52.0
^ permalink raw reply related
* Re: [PATCH 1/1] IMA event log trimming
From: Roberto Sassu @ 2025-12-09 9:36 UTC (permalink / raw)
To: James Bottomley, steven chen, linux-integrity
Cc: zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg, paul,
jmorris, serge, linux-security-module, anirudhve, gregorylumen,
nramas, sushring
In-Reply-To: <d0c00469a8501483baffaf1158102c0f2c5211e8.camel@HansenPartnership.com>
On Tue, 2025-12-09 at 07:17 +0900, James Bottomley wrote:
> On Mon, 2025-12-08 at 10:40 +0100, Roberto Sassu wrote:
> > I have the impression that none the functionality you cited has to be
> > implemented in the kernel, because the only component one can trust
> > to verify the integrity of the IMA measurements list is the TPM.
> > Whether either the kernel or user space retain the measurements is
> > irrelevant.
>
> That's correct, I'm not advocating moving quoting into the kernel. Co-
> ordinating the trim with where the quote gets you to is phenomenally
> useful. While you could theoretically store any mismatch in userspace,
> having two locations for the log makes it more error prone.
>
> > I believe that the only role of the kernel is to get rid of the
> > measurements entries as fast as possible (the kernel would act more
> > like a buffer).
>
> I wouldn't say that, I'd say to get rid of measurements that the user
> has indicated are of no further use.
Different users could have different and conflicting requirements, and
we would spend time trying to conciliate those. We can avoid that by
doing it the same for everyone, and the additional cost of handling it
I believe it is fair.
I could accept staging N entries since I already agreed with Gregory
and Steven, and since it requires only an extra iteration in the linked
list. The other desired functionality should be implemented in user
space.
> > This was actually the intent of my original proposal in
> > https://github.com/linux-integrity/linux/issues/1 . The idea of
> > staging (was snapshotting, but Mimi thinks the term is not accurate)
> > is simply to detach the entire IMA measurement list as fast as
> > possible. Further read and delete after staging is done without
> > interfering with new measurements (sure, the detaching of the hash
> > table is not yet as efficient as I hoped).
>
> From the application point of view, offloading the log and random
> points is a bit more work because now the log collector has to be
> modified to look in multiple locations and we'd also need an agreement
> of where those locations are and how the log is sequenced in a naming
> scheme so it's the same for every distribution. If the application is
> in charge of trimming the log at a particular point, collection remains
> the same (it can simply be the existing in-kernel location), so we
> don't need a cross distro agreement, and the trim can simply be added
> as an extra function.
It could be a single location, the user space program would be
responsible to present the IMA measurement list as if it was never
trimmed.
Roberto
^ permalink raw reply
* Re: [PATCH 4/4] tpm-buf: Remove tpm_buf_append_handle
From: Jarkko Sakkinen @ 2025-12-09 7:52 UTC (permalink / raw)
To: linux-integrity
Cc: Peter Huewe, Jason Gunthorpe, James Bottomley, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list, open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <20251209073903.767518-5-jarkko@kernel.org>
On Tue, Dec 09, 2025 at 09:39:02AM +0200, Jarkko Sakkinen wrote:
> Since the number of handles is fixed to a single handle, eliminate all uses
> of buf->handles and deduce values during compile-time.
>
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> ---
> drivers/char/tpm/tpm-buf.c | 25 -----------------------
> drivers/char/tpm/tpm2-cmd.c | 4 ++--
> drivers/char/tpm/tpm2-sessions.c | 4 ++--
> include/linux/tpm.h | 1 -
> security/keys/trusted-keys/trusted_tpm2.c | 2 +-
> 5 files changed, 5 insertions(+), 31 deletions(-)
>
> diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
> index 73be8a87b472..752c69b8a4f5 100644
> --- a/drivers/char/tpm/tpm-buf.c
> +++ b/drivers/char/tpm/tpm-buf.c
> @@ -40,7 +40,6 @@ static void __tpm_buf_reset(struct tpm_buf *buf, u16 buf_size, u16 tag, u32 ordi
> buf->flags = 0;
> buf->length = sizeof(*head);
> buf->capacity = buf_size - sizeof(*buf);
> - buf->handles = 0;
> head->tag = cpu_to_be16(tag);
> head->length = cpu_to_be32(sizeof(*head));
> head->ordinal = cpu_to_be32(ordinal);
> @@ -56,7 +55,6 @@ static void __tpm_buf_reset_sized(struct tpm_buf *buf, u16 buf_size)
> buf->flags = TPM_BUF_TPM2B;
> buf->length = 2;
> buf->capacity = buf_size - sizeof(*buf);
> - buf->handles = 0;
> buf->data[0] = 0;
> buf->data[1] = 0;
> }
> @@ -177,29 +175,6 @@ void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
> }
> EXPORT_SYMBOL_GPL(tpm_buf_append_u32);
>
> -/**
> - * tpm_buf_append_handle() - Add a handle
> - * @buf: &tpm_buf instance
> - * @handle: a TPM object handle
> - *
> - * Add a handle to the buffer, and increase the count tracking the number of
> - * handles in the command buffer. Works only for command buffers.
> - */
> -void tpm_buf_append_handle(struct tpm_buf *buf, u32 handle)
> -{
> - if (buf->flags & TPM_BUF_INVALID)
> - return;
> -
> - if (buf->flags & TPM_BUF_TPM2B) {
> - WARN(1, "tpm-buf: invalid type: TPM2B\n");
> - buf->flags |= TPM_BUF_INVALID;
> - return;
> - }
> -
> - tpm_buf_append_u32(buf, handle);
> - buf->handles++;
> -}
> -
> /**
> * tpm_buf_read() - Read from a TPM buffer
> * @buf: &tpm_buf instance
> diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> index 5b04e74b6377..d14e249831c7 100644
> --- a/drivers/char/tpm/tpm2-cmd.c
> +++ b/drivers/char/tpm/tpm2-cmd.c
> @@ -205,7 +205,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> return rc;
> tpm_buf_append_hmac_session(chip, buf, 0, NULL, 0);
> } else {
> - tpm_buf_append_handle(buf, pcr_idx);
> + tpm_buf_append_u32(buf, pcr_idx);
> tpm_buf_append_auth(chip, buf, NULL, 0);
> }
>
> @@ -281,7 +281,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> TPM2_SA_CONTINUE_SESSION,
> NULL, 0);
> } else {
> - offset = buf->handles * 4 + TPM_HEADER_SIZE;
> + offset = TPM_HEADER_SIZE + sizeof(u32);
> head = (struct tpm_header *)buf->data;
> if (tpm_buf_length(buf) == offset)
> head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
> diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
> index 62a200ae72d7..dfd1abe673bc 100644
> --- a/drivers/char/tpm/tpm2-sessions.c
> +++ b/drivers/char/tpm/tpm2-sessions.c
> @@ -261,7 +261,7 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
> }
>
> if (!tpm2_chip_auth(chip)) {
> - tpm_buf_append_handle(buf, handle);
> + tpm_buf_append_u32(buf, handle);
> return 0;
> }
>
> @@ -289,7 +289,7 @@ void tpm_buf_append_auth(struct tpm_chip *chip, struct tpm_buf *buf,
> u8 *passphrase, int passphrase_len)
> {
> /* offset tells us where the sessions area begins */
> - int offset = buf->handles * 4 + TPM_HEADER_SIZE;
> + int offset = TPM_HEADER_SIZE + sizeof(u32);
> u32 len = 9 + passphrase_len;
>
> if (tpm_buf_length(buf) != offset) {
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index db716841973f..e20647cc0a0f 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -437,7 +437,6 @@ void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value);
> u8 tpm_buf_read_u8(struct tpm_buf *buf, off_t *offset);
> u16 tpm_buf_read_u16(struct tpm_buf *buf, off_t *offset);
> u32 tpm_buf_read_u32(struct tpm_buf *buf, off_t *offset);
> -void tpm_buf_append_handle(struct tpm_buf *buf, u32 handle);
>
> /*
> * Check if TPM device is in the firmware upgrade mode.
> diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> index 7756aa839d81..0a07a18da2ed 100644
> --- a/security/keys/trusted-keys/trusted_tpm2.c
> +++ b/security/keys/trusted-keys/trusted_tpm2.c
> @@ -495,7 +495,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> if (tpm2_chip_auth(chip)) {
> tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT, NULL, 0);
> } else {
> - offset = buf->handles * 4 + TPM_HEADER_SIZE;
> + offset = TPM_HEADER_SIZE + sizeof(u32);
Maybe it would be sensible to define:
/*
* Offset of the authorization area of a TPM command with a single
* handle.
*/
#define TPM2_AUTH_OFFSET (TPM_HEADER_SIZE + sizeof(u32))
This would overall clarify how this code works.
> head = (struct tpm_header *)buf->data;
> if (tpm_buf_length(buf) == offset)
> head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
> --
> 2.52.0
>
BR, Jarkko
^ permalink raw reply
* [PATCH 4/4] tpm-buf: Remove tpm_buf_append_handle
From: Jarkko Sakkinen @ 2025-12-09 7:39 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, James Bottomley,
Mimi Zohar, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM
In-Reply-To: <20251209073903.767518-1-jarkko@kernel.org>
Since the number of handles is fixed to a single handle, eliminate all uses
of buf->handles and deduce values during compile-time.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
drivers/char/tpm/tpm-buf.c | 25 -----------------------
drivers/char/tpm/tpm2-cmd.c | 4 ++--
drivers/char/tpm/tpm2-sessions.c | 4 ++--
include/linux/tpm.h | 1 -
security/keys/trusted-keys/trusted_tpm2.c | 2 +-
5 files changed, 5 insertions(+), 31 deletions(-)
diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
index 73be8a87b472..752c69b8a4f5 100644
--- a/drivers/char/tpm/tpm-buf.c
+++ b/drivers/char/tpm/tpm-buf.c
@@ -40,7 +40,6 @@ static void __tpm_buf_reset(struct tpm_buf *buf, u16 buf_size, u16 tag, u32 ordi
buf->flags = 0;
buf->length = sizeof(*head);
buf->capacity = buf_size - sizeof(*buf);
- buf->handles = 0;
head->tag = cpu_to_be16(tag);
head->length = cpu_to_be32(sizeof(*head));
head->ordinal = cpu_to_be32(ordinal);
@@ -56,7 +55,6 @@ static void __tpm_buf_reset_sized(struct tpm_buf *buf, u16 buf_size)
buf->flags = TPM_BUF_TPM2B;
buf->length = 2;
buf->capacity = buf_size - sizeof(*buf);
- buf->handles = 0;
buf->data[0] = 0;
buf->data[1] = 0;
}
@@ -177,29 +175,6 @@ void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
}
EXPORT_SYMBOL_GPL(tpm_buf_append_u32);
-/**
- * tpm_buf_append_handle() - Add a handle
- * @buf: &tpm_buf instance
- * @handle: a TPM object handle
- *
- * Add a handle to the buffer, and increase the count tracking the number of
- * handles in the command buffer. Works only for command buffers.
- */
-void tpm_buf_append_handle(struct tpm_buf *buf, u32 handle)
-{
- if (buf->flags & TPM_BUF_INVALID)
- return;
-
- if (buf->flags & TPM_BUF_TPM2B) {
- WARN(1, "tpm-buf: invalid type: TPM2B\n");
- buf->flags |= TPM_BUF_INVALID;
- return;
- }
-
- tpm_buf_append_u32(buf, handle);
- buf->handles++;
-}
-
/**
* tpm_buf_read() - Read from a TPM buffer
* @buf: &tpm_buf instance
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 5b04e74b6377..d14e249831c7 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -205,7 +205,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
return rc;
tpm_buf_append_hmac_session(chip, buf, 0, NULL, 0);
} else {
- tpm_buf_append_handle(buf, pcr_idx);
+ tpm_buf_append_u32(buf, pcr_idx);
tpm_buf_append_auth(chip, buf, NULL, 0);
}
@@ -281,7 +281,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
TPM2_SA_CONTINUE_SESSION,
NULL, 0);
} else {
- offset = buf->handles * 4 + TPM_HEADER_SIZE;
+ offset = TPM_HEADER_SIZE + sizeof(u32);
head = (struct tpm_header *)buf->data;
if (tpm_buf_length(buf) == offset)
head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 62a200ae72d7..dfd1abe673bc 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -261,7 +261,7 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
}
if (!tpm2_chip_auth(chip)) {
- tpm_buf_append_handle(buf, handle);
+ tpm_buf_append_u32(buf, handle);
return 0;
}
@@ -289,7 +289,7 @@ void tpm_buf_append_auth(struct tpm_chip *chip, struct tpm_buf *buf,
u8 *passphrase, int passphrase_len)
{
/* offset tells us where the sessions area begins */
- int offset = buf->handles * 4 + TPM_HEADER_SIZE;
+ int offset = TPM_HEADER_SIZE + sizeof(u32);
u32 len = 9 + passphrase_len;
if (tpm_buf_length(buf) != offset) {
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index db716841973f..e20647cc0a0f 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -437,7 +437,6 @@ void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value);
u8 tpm_buf_read_u8(struct tpm_buf *buf, off_t *offset);
u16 tpm_buf_read_u16(struct tpm_buf *buf, off_t *offset);
u32 tpm_buf_read_u32(struct tpm_buf *buf, off_t *offset);
-void tpm_buf_append_handle(struct tpm_buf *buf, u32 handle);
/*
* Check if TPM device is in the firmware upgrade mode.
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 7756aa839d81..0a07a18da2ed 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -495,7 +495,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
if (tpm2_chip_auth(chip)) {
tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT, NULL, 0);
} else {
- offset = buf->handles * 4 + TPM_HEADER_SIZE;
+ offset = TPM_HEADER_SIZE + sizeof(u32);
head = (struct tpm_header *)buf->data;
if (tpm_buf_length(buf) == offset)
head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
--
2.52.0
^ permalink raw reply related
* [PATCH 2/4] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-09 7:39 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, James Bottomley,
Mimi Zohar, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM
In-Reply-To: <20251209073903.767518-1-jarkko@kernel.org>
tpm2_load_cmd() and tpm2_unseal_cmd() use the same parent, and calls to
tpm_buf_append_name() cause the exact same TPM2_ReadPublic command to be
sent to the chip, causing unnecessary traffic.
1. Export tpm2_read_public in order to make it callable from
'trusted_tpm2'.
2. Re-orchestrate tpm2_seal_trusted() and tpm2_unseal_trusted() in order to
halve the name resolutions required:
2a. Move tpm2_read_public() calls into trusted_tpm2.
2b. Pass TPM name to tpm_buf_append_name().
2c. Rework tpm_buf_append_name() to use the pre-resolved name.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
drivers/char/tpm/tpm2-cmd.c | 3 +-
drivers/char/tpm/tpm2-sessions.c | 95 +++++------------
include/linux/tpm.h | 10 +-
security/keys/trusted-keys/trusted_tpm2.c | 124 ++++++++++++++--------
4 files changed, 118 insertions(+), 114 deletions(-)
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 2682f5ec09cb..5b04e74b6377 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -199,7 +199,8 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
if (!disable_pcr_integrity) {
- rc = tpm_buf_append_name(chip, buf, pcr_idx, NULL);
+ rc = tpm_buf_append_name(chip, buf, pcr_idx, (u8 *)&pcr_idx,
+ sizeof(u32));
if (rc)
return rc;
tpm_buf_append_hmac_session(chip, buf, 0, NULL, 0);
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index a0c88fb1804c..0816a91134fc 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -136,8 +136,8 @@ struct tpm2_auth {
* handle, but they are part of the session by name, which
* we must compute and remember
*/
- u32 name_h[AUTH_MAX_NAMES];
u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
+ u16 name_size_tbl[AUTH_MAX_NAMES];
};
#ifdef CONFIG_TCG_TPM2_HMAC
@@ -163,7 +163,17 @@ static int name_size(const u8 *name)
}
}
-static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+/**
+ * tpm2_read_public: Resolve TPM name for a handle
+ * @chip: TPM chip to use.
+ * @handle: TPM handle.
+ * @name: A buffer for returning the name blob. Must have a
+ * capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
+ *
+ * Returns size of TPM handle name of success.
+ * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
+ */
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
{
u32 mso = tpm2_handle_mso(handle);
off_t offset = TPM_HEADER_SIZE;
@@ -212,14 +222,16 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
memcpy(name, &buf->data[offset], rc);
return name_size_alg;
}
+EXPORT_SYMBOL_GPL(tpm2_read_public);
#endif /* CONFIG_TCG_TPM2_HMAC */
/**
- * tpm_buf_append_name() - add a handle area to the buffer
- * @chip: the TPM chip structure
- * @buf: The buffer to be appended
- * @handle: The handle to be appended
- * @name: The name of the handle (may be NULL)
+ * tpm_buf_append_name() - Append a handle and store TPM name
+ * @chip: TPM chip to use.
+ * @buf: TPM buffer containing the TPM command in-transit.
+ * @handle: TPM handle to be appended.
+ * @name: TPM name of the handle
+ * @name_size: Size of the TPM name.
*
* In order to compute session HMACs, we need to know the names of the
* objects pointed to by the handles. For most objects, this is simply
@@ -236,15 +248,14 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
* will be caused by an incorrect programming model and indicated by a
* kernel message.
*
- * Ends the authorization session on failure.
+ * Returns zero on success.
+ * Returns -EIO when the authorization area state is malformed.
*/
int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name)
+ u32 handle, u8 *name, u16 name_size)
{
#ifdef CONFIG_TCG_TPM2_HMAC
- enum tpm2_mso_type mso = tpm2_handle_mso(handle);
struct tpm2_auth *auth;
- u16 name_size_alg;
int slot;
int ret;
#endif
@@ -269,36 +280,15 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
}
tpm_buf_append_u32(buf, handle);
auth->session += 4;
-
- if (mso == TPM2_MSO_PERSISTENT ||
- mso == TPM2_MSO_VOLATILE ||
- mso == TPM2_MSO_NVRAM) {
- if (!name) {
- ret = tpm2_read_public(chip, handle, auth->name[slot]);
- if (ret < 0)
- goto err;
-
- name_size_alg = ret;
- }
- } else {
- if (name) {
- dev_err(&chip->dev, "handle 0x%08x does not use a name\n",
- handle);
- ret = -EIO;
- goto err;
- }
- }
-
- auth->name_h[slot] = handle;
- if (name)
- memcpy(auth->name[slot], name, name_size_alg);
+ memcpy(auth->name[slot], name, name_size);
+ auth->name_size_tbl[slot] = name_size;
#endif
return 0;
#ifdef CONFIG_TCG_TPM2_HMAC
err:
tpm2_end_auth_session(chip);
- return tpm_ret_to_err(ret);
+ return ret;
#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -606,22 +596,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
attrs = chip->cc_attrs_tbl[i];
handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
+ offset_s += handles * sizeof(u32);
- /*
- * just check the names, it's easy to make mistakes. This
- * would happen if someone added a handle via
- * tpm_buf_append_u32() instead of tpm_buf_append_name()
- */
- for (i = 0; i < handles; i++) {
- u32 handle = tpm_buf_read_u32(buf, &offset_s);
-
- if (auth->name_h[i] != handle) {
- dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
- ret = -EIO;
- goto err;
- }
- }
- /* point offset_s to the start of the sessions */
val = tpm_buf_read_u32(buf, &offset_s);
/* point offset_p to the start of the parameters */
offset_p = offset_s + val;
@@ -682,23 +658,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
/* ordinal is already BE */
sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
/* add the handle names */
- for (i = 0; i < handles; i++) {
- enum tpm2_mso_type mso = tpm2_handle_mso(auth->name_h[i]);
-
- if (mso == TPM2_MSO_PERSISTENT ||
- mso == TPM2_MSO_VOLATILE ||
- mso == TPM2_MSO_NVRAM) {
- ret = name_size(auth->name[i]);
- if (ret < 0)
- goto err;
-
- sha256_update(&sctx, auth->name[i], ret);
- } else {
- __be32 h = cpu_to_be32(auth->name_h[i]);
-
- sha256_update(&sctx, (u8 *)&h, 4);
- }
- }
+ for (i = 0; i < handles; i++)
+ sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
if (offset_s != tpm_buf_length(buf))
sha256_update(&sctx, &buf->data[offset_s],
tpm_buf_length(buf) - offset_s);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index b4b5bd586501..db716841973f 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -539,7 +539,7 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
}
int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name);
+ u32 handle, u8 *name, u16 name_size);
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
u8 attributes, u8 *passphrase,
int passphraselen);
@@ -553,6 +553,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
int rc);
void tpm2_end_auth_session(struct tpm_chip *chip);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
#else
#include <linux/unaligned.h>
@@ -576,6 +577,13 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
{
return rc;
}
+
+static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle,
+ void *name)
+{
+ memcpy(name, &handle, sizeof(u32));
+ return sizeof(u32);
+}
#endif /* CONFIG_TCG_TPM2_HMAC */
#endif
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 9074ae1a5896..7756aa839d81 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -203,7 +203,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
+ u8 parent_name[TPM2_MAX_NAME_SIZE];
off_t offset = TPM_HEADER_SIZE;
+ u16 parent_name_size;
int blob_len = 0;
int hash;
u32 flags;
@@ -220,6 +222,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
if (rc)
return rc;
+ rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+ if (rc < 0)
+ goto out_put;
+
+ parent_name_size = rc;
+
rc = tpm2_start_auth_session(chip);
if (rc)
goto out_put;
@@ -234,7 +242,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
- rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+ parent_name_size);
if (rc)
goto out_put;
@@ -326,48 +335,38 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
}
/**
- * tpm2_load_cmd() - execute a TPM2_Load command
- *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: returned blob handle
+ * tpm2_load_cmd() - Execute TPM2_Load
+ * @chip: TPM chip to use.
+ * @payload: Key data in clear text.
+ * @options: Trusted key options.
+ * @parent_name: A cryptographic name, i.e. a TPMT_HA blob, of the
+ * parent key.
+ * @blob: The decoded payload for the key.
+ * @blob_handle: On success, will contain handle to the loaded keyedhash
+ * blob.
*
- * Return: 0 on success.
- * -E2BIG on wrong payload size.
- * -EPERM on tpm error status.
- * < 0 error from tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
*/
static int tpm2_load_cmd(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options,
+ u8 *parent_name,
+ u16 parent_name_size,
+ const u8 *blob,
u32 *blob_handle)
{
u8 *blob_ref __free(kfree) = NULL;
unsigned int private_len;
unsigned int public_len;
unsigned int blob_len;
- u8 *blob, *pub;
+ const u8 *pub;
int rc;
u32 attrs;
- rc = tpm2_key_decode(payload, options, &blob);
- if (rc) {
- /* old form */
- blob = payload->blob;
- payload->old_format = 1;
- } else {
- /* Bind for cleanup: */
- blob_ref = blob;
- }
-
- /* new format carries keyhandle but old format doesn't */
- if (!options->keyhandle)
- return -EINVAL;
-
/* must be big enough for at least the two be16 size counts */
if (payload->blob_len < 4)
- return -EINVAL;
+ return -E2BIG;
private_len = get_unaligned_be16(blob);
@@ -406,7 +405,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
- rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+ parent_name_size);
if (rc)
return rc;
@@ -434,20 +434,23 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
}
/**
- * tpm2_unseal_cmd() - execute a TPM2_Unload command
+ * tpm2_unseal_cmd() - Execute TPM2_Unload
*
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: blob handle
+ * @chip: TPM chip to use
+ * @payload: Key data in clear text.
+ * @options: Trusted key options.
+ * @parent_name: A cryptographic name, i.e. a TPMT_HA blob, of the
+ * parent key.
+ * @blob_handle: Handle to the loaded keyedhash blob.
*
- * Return: 0 on success
- * -EPERM on tpm error status
- * < 0 error from tpm_send
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
*/
static int tpm2_unseal_cmd(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options,
+ u8 *parent_name,
+ u16 parent_name_size,
u32 blob_handle)
{
struct tpm_header *head;
@@ -467,7 +470,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
- rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+ parent_name_size);
if (rc)
return rc;
@@ -534,30 +538,60 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
}
/**
- * tpm2_unseal_trusted() - unseal the payload of a trusted key
+ * tpm2_unseal_trusted() - Unseal a trusted key
+ * @chip: TPM chip to use.
+ * @payload: Key data in clear text.
+ * @options: Trusted key options.
*
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- *
- * Return: Same as with tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Return -EINVAL when parent's key handle has not been set.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load or TPM2_Unseal
+ * fails.
*/
int tpm2_unseal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
+ u8 *blob_ref __free(kfree) = NULL;
+ u8 parent_name[TPM2_MAX_NAME_SIZE];
+ u16 parent_name_size;
u32 blob_handle;
+ u8 *blob;
int rc;
+ /*
+ * Try to decode the provided blob as an ASN.1 blob. Assume that the
+ * blob is in the legacy format if decoding does not end successfully.
+ */
+ rc = tpm2_key_decode(payload, options, &blob);
+ if (rc) {
+ blob = payload->blob;
+ payload->old_format = 1;
+ } else {
+ blob_ref = blob;
+ }
+
+ if (!options->keyhandle)
+ return -EINVAL;
+
rc = tpm_try_get_ops(chip);
if (rc)
return rc;
- rc = tpm2_load_cmd(chip, payload, options, &blob_handle);
+ rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+ if (rc < 0)
+ goto out;
+
+ parent_name_size = rc;
+
+ rc = tpm2_load_cmd(chip, payload, options, parent_name,
+ parent_name_size, blob, &blob_handle);
if (rc)
goto out;
- rc = tpm2_unseal_cmd(chip, payload, options, blob_handle);
+ rc = tpm2_unseal_cmd(chip, payload, options, parent_name,
+ parent_name_size, blob_handle);
+
tpm2_flush_context(chip, blob_handle);
out:
--
2.52.0
^ permalink raw reply related
* Re: [PATCH] KEYS: trusted: Don't use 'buf->handles'
From: Jarkko Sakkinen @ 2025-12-09 7:19 UTC (permalink / raw)
To: linux-integrity
Cc: James Bottomley, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20251209071144.56893-1-jarkko@kernel.org>
On Tue, Dec 09, 2025 at 09:11:43AM +0200, Jarkko Sakkinen wrote:
> tpm2_unseal_trusted() deduces number of handles in run-time even tho the
> expected value is known at compile time. Address the issue.
>
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> ---
> security/keys/trusted-keys/trusted_tpm2.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> index 9074ae1a5896..e78061ee2d99 100644
> --- a/security/keys/trusted-keys/trusted_tpm2.c
> +++ b/security/keys/trusted-keys/trusted_tpm2.c
> @@ -491,7 +491,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> if (tpm2_chip_auth(chip)) {
> tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT, NULL, 0);
> } else {
> - offset = buf->handles * 4 + TPM_HEADER_SIZE;
> + offset = TPM_HEADER_SIZE + 2 * sizeof(u32);
Oops.
Should be 'TPM_HEADER_SIZE + sizeof(u32)'. There's just a single handle.
> head = (struct tpm_header *)buf->data;
> if (tpm_buf_length(buf) == offset)
> head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
> --
> 2.39.5
>
BR, Jarkko
^ permalink raw reply
* [PATCH] KEYS: trusted: Don't use 'buf->handles'
From: Jarkko Sakkinen @ 2025-12-09 7:11 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, James Bottomley, Mimi Zohar, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM, open list
tpm2_unseal_trusted() deduces number of handles in run-time even tho the
expected value is known at compile time. Address the issue.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
security/keys/trusted-keys/trusted_tpm2.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 9074ae1a5896..e78061ee2d99 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -491,7 +491,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
if (tpm2_chip_auth(chip)) {
tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT, NULL, 0);
} else {
- offset = buf->handles * 4 + TPM_HEADER_SIZE;
+ offset = TPM_HEADER_SIZE + 2 * sizeof(u32);
head = (struct tpm_header *)buf->data;
if (tpm_buf_length(buf) == offset)
head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
--
2.39.5
^ permalink raw reply related
* Re: [PATCH 1/1] IMA event log trimming
From: James Bottomley @ 2025-12-08 22:17 UTC (permalink / raw)
To: Roberto Sassu, steven chen, linux-integrity
Cc: zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg, paul,
jmorris, serge, linux-security-module, anirudhve, gregorylumen,
nramas, sushring
In-Reply-To: <1ca00e3238e804db9280abf8655364c2662754ca.camel@huaweicloud.com>
On Mon, 2025-12-08 at 10:40 +0100, Roberto Sassu wrote:
> I have the impression that none the functionality you cited has to be
> implemented in the kernel, because the only component one can trust
> to verify the integrity of the IMA measurements list is the TPM.
> Whether either the kernel or user space retain the measurements is
> irrelevant.
That's correct, I'm not advocating moving quoting into the kernel. Co-
ordinating the trim with where the quote gets you to is phenomenally
useful. While you could theoretically store any mismatch in userspace,
having two locations for the log makes it more error prone.
> I believe that the only role of the kernel is to get rid of the
> measurements entries as fast as possible (the kernel would act more
> like a buffer).
I wouldn't say that, I'd say to get rid of measurements that the user
has indicated are of no further use.
> This was actually the intent of my original proposal in
> https://github.com/linux-integrity/linux/issues/1 . The idea of
> staging (was snapshotting, but Mimi thinks the term is not accurate)
> is simply to detach the entire IMA measurement list as fast as
> possible. Further read and delete after staging is done without
> interfering with new measurements (sure, the detaching of the hash
> table is not yet as efficient as I hoped).
From the application point of view, offloading the log and random
points is a bit more work because now the log collector has to be
modified to look in multiple locations and we'd also need an agreement
of where those locations are and how the log is sequenced in a naming
scheme so it's the same for every distribution. If the application is
in charge of trimming the log at a particular point, collection remains
the same (it can simply be the existing in-kernel location), so we
don't need a cross distro agreement, and the trim can simply be added
as an extra function.
Regards,
James
^ permalink raw reply
* Re: [PATCH] ipe: remove headers that are included but not used
From: Fan Wu @ 2025-12-08 19:02 UTC (permalink / raw)
To: Yicong Hui
Cc: Fan Wu, paul, jmorris, serge, linux-security-module, linux-kernel,
skhan, david.hunter.linux
In-Reply-To: <3748b276-6503-4c50-a394-e5b75c1eb7bf@gmail.com>
On Sat, Dec 6, 2025 at 1:04 PM Yicong Hui <yiconghui@gmail.com> wrote:
>
> On 12/3/25 10:25 PM, Fan Wu wrote:
> > On Wed, Dec 3, 2025 at 11:37 AM Yicong Hui <yiconghui@gmail.com> wrote:
> >
> > Hi Yicong,
> >
> > Thanks for the patch. This kind of cleanup is appreciated.
> >
> > Commit message typo: "audit. c, audit.c, policy. c" - audit. c is listed
> > twice.
>
> Hi! Thank you for the reply! Yes! My bad, this typo will be fixed in v2.
>
...
>
> I have manually read through the functions/macros/filetypes in policy.c,
> policy_fs.c and audit.c and found a few dependencies that are used but
> not explicitly included, like minmax.h, sha2.h, lockdep.h, string.h,
> capability.h, kstrtox.h, sprintf.h, array_size.h and err.h.
>
> This might be a stupid question, but how explicit should I be in my v2
> patch with the dependencies? There's headers like
> "asm-generic/int-ll64.h" "uidgid.h", "gfp_types.h", "rwonce.h",
> "compiler_types.h" or "errno-base.h" but I'm not sure to what extent I
> need to import them, because I shouldn't be including them all, right?
Hi Yicong,
On second thought, the cost of this cleanup outweighs the benefit.
Let's drop this patch and keep the code as it is.
-Fan
^ permalink raw reply
* Re: [PATCH] KEYS: trusted: Fix overwrite of keyhandle parameter
From: Jarkko Sakkinen @ 2025-12-08 18:56 UTC (permalink / raw)
To: linux-integrity
Cc: stable, James Bottomley, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20251208145436.21519-1-jarkko@kernel.org>
On Mon, Dec 08, 2025 at 04:54:35PM +0200, Jarkko Sakkinen wrote:
> tpm2_key_decode() overrides the explicit keyhandle parameter, which can
> lead to problems, if the loaded parent handle does not match the handle
> stored to the key file. This can easily happen as handle by definition
> is an ambiguous attribute.
>
> Cc: stable@vger.kernel.org # v5.13+
> Fixes: f2219745250f ("security: keys: trusted: use ASN.1 TPM2 key format for the blobs")
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
What this means in practice is that sometimes you need either to:
1. Binary patch the key file.
2. Decompose/compose a key file
BR, Jarkko
^ permalink raw reply
* Re: [PATCH 1/1] IMA event log trimming
From: Roberto Sassu @ 2025-12-08 17:50 UTC (permalink / raw)
To: Gregory Lumen
Cc: James Bottomley, steven chen, linux-integrity, zohar,
roberto.sassu, dmitry.kasatkin, eric.snowberg, paul, jmorris,
serge, linux-security-module, anirudhve, nramas, sushring
In-Reply-To: <862fa081-df51-084-ae2c-efa0eae0ca7e@linux.microsoft.com>
On Mon, 2025-12-08 at 09:21 -0800, Gregory Lumen wrote:
> > Rather than designing the interface absent use cases, could we give use
> > cases first so we know it fits?
>
> I would also like to request that we include operational considerations in
> the interface design; specifically, which additional signals can or should
> be made available to UM to assist in diagnosing log validation failures.
> This seems called for as any form of log trimming will introduce a new
> potential cause for validation failures (unexpected trimming).
>
> With the proposed changes, the only signal available to system operators
> is the validation failure itself, with no signal that could be used to
> determine if the failure was the result of an unexpected trim or a loss of
> synchronization between the log and the PCRs (either through an unexpected
> PCR extend, or tampering with the measurement list). Any of these may
> indicate malicious activity, but they may also result from system
> configuration issues that operators would need to diagnose and resolve.
I don't agree with this. User space can keep a persistent state, and
resume from where it was interrupted. There is no risk of loss of data,
if user space deletes staged measurements only after the read is
complete.
The same comment applies for coordination between different user space
agents. It would not be too hard to expose an interface managed by a
new user space component, providing similar data as IMA (even showing
the full measurements list without its users being aware of the
snapshots). The other agents can fetch the data from the new interface.
And also, I don't see the problem in developing more complex user space
programs which link the measurement entries to PCR values, and anything
that is desirable.
And if from the kernel perspective all we need to do is a list replace,
for me that is the most efficient solution.
Roberto
> Tracking and exposing either the total number of trimmed measurements or
> the most recent trimmed-to PCR values by the kernel would allow system
> operators to determine whether a failure was caused by unexpected trimming
> or integrity issues. (Storing the PCR values also enables validation of
> the retained measurements even after an unexpected trim, though I’m unsure
> how often that signal would prove useful.)
>
> Neither approach appears to add any additional attack surface beyond
> raising the likelihood of incorrectly or insecurely implemented UM
> attestation agents. Though that risk (and the additional kernel
> complexity) should be weighed against the value of the additional
> diagnostic signals.
>
> -Gregory Lumen
^ permalink raw reply
* Re: [PATCH 1/1] IMA event log trimming
From: Gregory Lumen @ 2025-12-08 17:21 UTC (permalink / raw)
To: Roberto Sassu
Cc: James Bottomley, steven chen, linux-integrity, zohar,
roberto.sassu, dmitry.kasatkin, eric.snowberg, paul, jmorris,
serge, linux-security-module, anirudhve, nramas, sushring
In-Reply-To: <1ca00e3238e804db9280abf8655364c2662754ca.camel@huaweicloud.com>
[-- Attachment #1: Type: text/plain, Size: 1701 bytes --]
> Rather than designing the interface absent use cases, could we give use
> cases first so we know it fits?
I would also like to request that we include operational considerations in
the interface design; specifically, which additional signals can or should
be made available to UM to assist in diagnosing log validation failures.
This seems called for as any form of log trimming will introduce a new
potential cause for validation failures (unexpected trimming).
With the proposed changes, the only signal available to system operators
is the validation failure itself, with no signal that could be used to
determine if the failure was the result of an unexpected trim or a loss of
synchronization between the log and the PCRs (either through an unexpected
PCR extend, or tampering with the measurement list). Any of these may
indicate malicious activity, but they may also result from system
configuration issues that operators would need to diagnose and resolve.
Tracking and exposing either the total number of trimmed measurements or
the most recent trimmed-to PCR values by the kernel would allow system
operators to determine whether a failure was caused by unexpected trimming
or integrity issues. (Storing the PCR values also enables validation of
the retained measurements even after an unexpected trim, though I’m unsure
how often that signal would prove useful.)
Neither approach appears to add any additional attack surface beyond
raising the likelihood of incorrectly or insecurely implemented UM
attestation agents. Though that risk (and the additional kernel
complexity) should be weighed against the value of the additional
diagnostic signals.
-Gregory Lumen
^ permalink raw reply
* [PATCH] KEYS: trusted: Fix overwrite of keyhandle parameter
From: Jarkko Sakkinen @ 2025-12-08 14:54 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, stable, James Bottomley, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM, open list
tpm2_key_decode() overrides the explicit keyhandle parameter, which can
lead to problems, if the loaded parent handle does not match the handle
stored to the key file. This can easily happen as handle by definition
is an ambiguous attribute.
Cc: stable@vger.kernel.org # v5.13+
Fixes: f2219745250f ("security: keys: trusted: use ASN.1 TPM2 key format for the blobs")
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
security/keys/trusted-keys/trusted_tpm2.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index fb76c4ea496f..950684e54c71 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -121,7 +121,9 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
return -ENOMEM;
*buf = blob;
- options->keyhandle = ctx.parent;
+
+ if (!options->keyhandle)
+ options->keyhandle = ctx.parent;
memcpy(blob, ctx.priv, ctx.priv_len);
blob += ctx.priv_len;
--
2.39.5
^ permalink raw reply related
* Re: [PATCH 1/1] IMA event log trimming
From: Roberto Sassu @ 2025-12-08 9:40 UTC (permalink / raw)
To: James Bottomley, steven chen, linux-integrity
Cc: zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg, paul,
jmorris, serge, linux-security-module, anirudhve, gregorylumen,
nramas, sushring
In-Reply-To: <34d739c2cf15baf78dff5acb7ae3ddd7ad47f219.camel@HansenPartnership.com>
On Fri, 2025-12-05 at 13:30 -0500, James Bottomley wrote:
> On Fri, 2025-12-05 at 10:30 +0100, Roberto Sassu wrote:
> > On Tue, 2025-12-02 at 15:28 -0800, steven chen wrote:
> > > This patch is for trimming N entries of the IMA event logs as well
> > > as cleaning the hash table.
> > >
> > > It provides a userspace interface ima_trim_log that can be used to
> > > input number N to let kernel to trim N entries of IMA event logs.
> > > When read this interface, it returns number of entries trimmed last
> > > tim.
> >
> > High-level comments:
> > - It does not offer the possibility to keep the hash table
> > - There is no coordination between taking a snapshot and the readers
> > of
> > the measurements list (I think it is necessary, since reading is
> > based on *pos, which contains the entries read until a given point;
> > if there is a truncate in the middle of the read, *pos would still
> > refer to the non-truncated list and the next read will skip some
> > measurement entries)
>
> Rather than designing the interface absent use cases, could we give use
> cases first so we know it fits? I really only have one: the keylime
> agent, but I believe it's pattern, which would be get TPM quote of
> logging PCRs, certify quote and then trim the log up to the point the
> quote was obtained will be a common pattern (at least I don't think
> anyone would trim the log without quoting it). If you're trimming
> based on a quote, you know the end hash of all the PCRs and you want to
> trim up to that. Since the log is fixed, you can work out what the
> index offset is, but it does seem a bit suboptimal to have to compute
> that, which is why I think trimming by end PCR hash is useful. The
> main point in all of this is, I think, that you don't really get to
> decide which point in the log the TPM will quote: it always quotes it's
> current PCR values, so no-one who cares about log quotes can select the
> number of entries first, they all have to see what PCR values the quote
> returns.
>
> > - While trimming per se is ok, I like more the idea of staging
> > changes and letting the user delete the staged measurements list
> > later
>
> I'm not averse to this, but keylime won't care ... it gets the quote
> and the log and it will verify the log before it will get the agent to
> issue a trim. Is there a use case that would actually need this
> behaviour?
>
> I also think having the base PCRs stored in the kernel after the trim
> (in a way that's easily consumed, like a non measured log entry), thus
> allowing verification from the log up to any current quote is useful.
> The reason I think it's useful is the attestation token one: I can see
> the keylime verifier giving an I've verified the log up to here and all
> the entries look good token that contains the base PCRs and if there
> are quite a few of these floating around (especially if the log isn't
> always trimmed) I could see the kernel base PCR storage being used to
> ask which is the relevant attestation token I should use to verify the
> rest of the log (or even used to detect nefarious trimming designed to
> hide records).
I have the impression that none the functionality you cited has to be
implemented in the kernel, because the only component one can trust to
verify the integrity of the IMA measurements list is the TPM. Whether
either the kernel or user space retain the measurements is irrelevant.
I believe that the only role of the kernel is to get rid of the
measurements entries as fast as possible (the kernel would act more
like a buffer).
This was actually the intent of my original proposal in
https://github.com/linux-integrity/linux/issues/1 . The idea of staging
(was snapshotting, but Mimi thinks the term is not accurate) is simply
to detach the entire IMA measurement list as fast as possible. Further
read and delete after staging is done without interfering with new
measurements (sure, the detaching of the hash table is not yet as
efficient as I hoped).
Anything that you said about verifying the measurements up to a certain
TPM quote can be implemented in user space without a decrease in
security. I don't see the need to synchronize the trim with the
verification in the kernel.
Roberto
^ permalink raw reply
* Re: [PATCH v2] net: ipv6: fix spelling typos in comments
From: ShiHao @ 2025-12-08 5:39 UTC (permalink / raw)
To: Simon Horman
Cc: kuba, davem, dsahern, edumazet, herbert, linux-kernel,
linux-security-module
In-Reply-To: <aTQCXJ7MUvnJpG6B@horms.kernel.org>
On Sat, Dec 06, 2025 at 10:15:56AM +0000, Simon Horman wrote:
> Unfortunately this patches do not apply cleanly on net-next,
> and thus can't be processed by our CI.
>
> When applying manually I see:
>
> Patch failed at 0001 net: ipv6: fix spelling typos in comments
> error: corrupt patch at line 58
>
> Also, net-next is currently closed.
>
> ## Form letter - net-next-closed
>
> The merge window for v6.19 has begun and therefore net-next has closed
> for new drivers, features, code refactoring and optimizations. We are
> currently accepting bug fixes only.
>
> Please repost when net-next reopens.
>
> Due to a combination of the merge-window, travel commitments of the
> maintainers, and the holiday season, net-next will re-open after
> 2nd January.
>
> RFC patches sent for review only are welcome at any time.
>
> See: https://www.kernel.org/doc/html/next/process/maintainer-netdev.html#development-cycle
Hi Simon
Thanks for letting me know about this i am still new to net-dev
so i did not know about this . I will make sure to take care of
this next time. Again everyone thanks for your time to this matter.
shihao
^ permalink raw reply
* Re: [PATCH v7 0/3] Optimize tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-08 5:15 UTC (permalink / raw)
To: linux-integrity
Cc: tpm2, James Bottomley, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20251208050620.339408-1-jarkko@kernel.org>
On Mon, Dec 08, 2025 at 07:06:16AM +0200, Jarkko Sakkinen wrote:
> The main goal is fairly straight-forwrd here.
>
> The aim of these patches is optimize the number of tpm2_read_public() calls
> to the bare minimum.
>
> ## About dropping 'parentName' attribute for ASN.1 keys from the patch set
>
> I wrote this section as a remainder as I have facts fresh in my mind so
> that I can return them as soon as there is working group for the ASN.1
> specification. We really need to have this in the spec.
>
> I dropped [1] given that [2] is landing shortly to IETF draft process,
> according to James Bottomley [3]. We will return to [1] as soon as draft
> process is open for comments. Still, that attribute is super important,
> and here is why.
>
> This will cause a overhead as tpm2_unseal_trusted needs to do an
> unnecessary (from pure technical perspective) TPM2_ReadPublic command to
> acquire TPM name of the parent. This is obviously known at the time of
> creation of a key but the information is not stored anywhere by the
> key format.
>
> It also aligns badly with TCG specifications as Table 6 of architecture
> spec explicitly defines a reference (or name) for transient keys,
> persistent keys and NV indexes to be TPM_ALG_ID concatenated together
> with the hash of TPMT_PUBLIC. I.e. the file format is using exactly
> the opposite what should be use as reference for keys than what it
> should use.
>
> Other benefits are of course auto-discovery of parent for a key file,
> which is nasty to do without the name pre-stored.
Right and TPMT_HA is calculated everytime when a trusted key is
created, and right after that the already calculated data is simply
thrown into dumpster. And we are talking about spec compliant way
to refer other keys here, and only 66 bytes of extra payload.
I don't get it. And neither do I get how anyone would want to fix
this issue with TPM2_CreatePrimary interception.
BR, Jarkko
^ permalink raw reply
* [PATCH v7 3/3] tpm2-sessions: Remove AUTH_MAX_NAMES
From: Jarkko Sakkinen @ 2025-12-08 5:06 UTC (permalink / raw)
To: linux-integrity
Cc: tpm2, Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe,
James Bottomley, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM
In-Reply-To: <20251208050620.339408-1-jarkko@kernel.org>
In all of the call sites only one session is ever append. Thus, reduce
AUTH_MAX_NAMES, which leads into removing constant completely.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v7:
- A new patch.
---
drivers/char/tpm/tpm2-sessions.c | 31 +++++++++++--------------------
1 file changed, 11 insertions(+), 20 deletions(-)
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 0816a91134fc..62a200ae72d7 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -72,9 +72,6 @@
#include <crypto/sha2.h>
#include <crypto/utils.h>
-/* maximum number of names the TPM must remember for authorization */
-#define AUTH_MAX_NAMES 3
-
#define AES_KEY_BYTES AES_KEYSIZE_128
#define AES_KEY_BITS (AES_KEY_BYTES*8)
@@ -136,8 +133,8 @@ struct tpm2_auth {
* handle, but they are part of the session by name, which
* we must compute and remember
*/
- u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
- u16 name_size_tbl[AUTH_MAX_NAMES];
+ u8 name[TPM2_MAX_NAME_SIZE];
+ u16 name_size;
};
#ifdef CONFIG_TCG_TPM2_HMAC
@@ -254,11 +251,14 @@ EXPORT_SYMBOL_GPL(tpm2_read_public);
int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
u32 handle, u8 *name, u16 name_size)
{
-#ifdef CONFIG_TCG_TPM2_HMAC
struct tpm2_auth *auth;
- int slot;
int ret;
-#endif
+
+ if (tpm_buf_length(buf) != TPM_HEADER_SIZE) {
+ dev_err(&chip->dev, "too many handles\n");
+ ret = -EIO;
+ goto err;
+ }
if (!tpm2_chip_auth(chip)) {
tpm_buf_append_handle(buf, handle);
@@ -266,12 +266,6 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
}
#ifdef CONFIG_TCG_TPM2_HMAC
- slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
- if (slot >= AUTH_MAX_NAMES) {
- dev_err(&chip->dev, "too many handles\n");
- ret = -EIO;
- goto err;
- }
auth = chip->auth;
if (auth->session != tpm_buf_length(buf)) {
dev_err(&chip->dev, "session state malformed");
@@ -280,16 +274,14 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
}
tpm_buf_append_u32(buf, handle);
auth->session += 4;
- memcpy(auth->name[slot], name, name_size);
- auth->name_size_tbl[slot] = name_size;
+ memcpy(auth->name, name, name_size);
+ auth->name_size = name_size;
#endif
return 0;
-#ifdef CONFIG_TCG_TPM2_HMAC
err:
tpm2_end_auth_session(chip);
return ret;
-#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -658,8 +650,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
/* ordinal is already BE */
sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
/* add the handle names */
- for (i = 0; i < handles; i++)
- sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
+ sha256_update(&sctx, auth->name, auth->name_size);
if (offset_s != tpm_buf_length(buf))
sha256_update(&sctx, &buf->data[offset_s],
tpm_buf_length(buf) - offset_s);
--
2.39.5
^ permalink raw reply related
* [PATCH v7 2/3] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-08 5:06 UTC (permalink / raw)
To: linux-integrity
Cc: tpm2, Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe,
James Bottomley, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM
In-Reply-To: <20251208050620.339408-1-jarkko@kernel.org>
tpm2_load_cmd() and tpm2_unseal_cmd() use the same parent, and calls to
tpm_buf_append_name() cause the exact same TPM2_ReadPublic command to be
sent to the chip, causing unnecessary traffic.
1. Export tpm2_read_public in order to make it callable from
'trusted_tpm2'.
2. Re-orchestrate tpm2_seal_trusted() and tpm2_unseal_trusted() in order to
halve the name resolutions required:
2a. Move tpm2_read_public() calls into trusted_tpm2.
2b. Pass TPM name to tpm_buf_append_name().
2c. Rework tpm_buf_append_name() to use the pre-resolved name.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v7:
- No changes of any of the versions so far.
---
drivers/char/tpm/tpm2-cmd.c | 3 +-
drivers/char/tpm/tpm2-sessions.c | 95 +++++------------
include/linux/tpm.h | 10 +-
security/keys/trusted-keys/trusted_tpm2.c | 124 ++++++++++++++--------
4 files changed, 118 insertions(+), 114 deletions(-)
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 2682f5ec09cb..5b04e74b6377 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -199,7 +199,8 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
if (!disable_pcr_integrity) {
- rc = tpm_buf_append_name(chip, buf, pcr_idx, NULL);
+ rc = tpm_buf_append_name(chip, buf, pcr_idx, (u8 *)&pcr_idx,
+ sizeof(u32));
if (rc)
return rc;
tpm_buf_append_hmac_session(chip, buf, 0, NULL, 0);
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index a0c88fb1804c..0816a91134fc 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -136,8 +136,8 @@ struct tpm2_auth {
* handle, but they are part of the session by name, which
* we must compute and remember
*/
- u32 name_h[AUTH_MAX_NAMES];
u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
+ u16 name_size_tbl[AUTH_MAX_NAMES];
};
#ifdef CONFIG_TCG_TPM2_HMAC
@@ -163,7 +163,17 @@ static int name_size(const u8 *name)
}
}
-static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+/**
+ * tpm2_read_public: Resolve TPM name for a handle
+ * @chip: TPM chip to use.
+ * @handle: TPM handle.
+ * @name: A buffer for returning the name blob. Must have a
+ * capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
+ *
+ * Returns size of TPM handle name of success.
+ * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
+ */
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
{
u32 mso = tpm2_handle_mso(handle);
off_t offset = TPM_HEADER_SIZE;
@@ -212,14 +222,16 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
memcpy(name, &buf->data[offset], rc);
return name_size_alg;
}
+EXPORT_SYMBOL_GPL(tpm2_read_public);
#endif /* CONFIG_TCG_TPM2_HMAC */
/**
- * tpm_buf_append_name() - add a handle area to the buffer
- * @chip: the TPM chip structure
- * @buf: The buffer to be appended
- * @handle: The handle to be appended
- * @name: The name of the handle (may be NULL)
+ * tpm_buf_append_name() - Append a handle and store TPM name
+ * @chip: TPM chip to use.
+ * @buf: TPM buffer containing the TPM command in-transit.
+ * @handle: TPM handle to be appended.
+ * @name: TPM name of the handle
+ * @name_size: Size of the TPM name.
*
* In order to compute session HMACs, we need to know the names of the
* objects pointed to by the handles. For most objects, this is simply
@@ -236,15 +248,14 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
* will be caused by an incorrect programming model and indicated by a
* kernel message.
*
- * Ends the authorization session on failure.
+ * Returns zero on success.
+ * Returns -EIO when the authorization area state is malformed.
*/
int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name)
+ u32 handle, u8 *name, u16 name_size)
{
#ifdef CONFIG_TCG_TPM2_HMAC
- enum tpm2_mso_type mso = tpm2_handle_mso(handle);
struct tpm2_auth *auth;
- u16 name_size_alg;
int slot;
int ret;
#endif
@@ -269,36 +280,15 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
}
tpm_buf_append_u32(buf, handle);
auth->session += 4;
-
- if (mso == TPM2_MSO_PERSISTENT ||
- mso == TPM2_MSO_VOLATILE ||
- mso == TPM2_MSO_NVRAM) {
- if (!name) {
- ret = tpm2_read_public(chip, handle, auth->name[slot]);
- if (ret < 0)
- goto err;
-
- name_size_alg = ret;
- }
- } else {
- if (name) {
- dev_err(&chip->dev, "handle 0x%08x does not use a name\n",
- handle);
- ret = -EIO;
- goto err;
- }
- }
-
- auth->name_h[slot] = handle;
- if (name)
- memcpy(auth->name[slot], name, name_size_alg);
+ memcpy(auth->name[slot], name, name_size);
+ auth->name_size_tbl[slot] = name_size;
#endif
return 0;
#ifdef CONFIG_TCG_TPM2_HMAC
err:
tpm2_end_auth_session(chip);
- return tpm_ret_to_err(ret);
+ return ret;
#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -606,22 +596,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
attrs = chip->cc_attrs_tbl[i];
handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
+ offset_s += handles * sizeof(u32);
- /*
- * just check the names, it's easy to make mistakes. This
- * would happen if someone added a handle via
- * tpm_buf_append_u32() instead of tpm_buf_append_name()
- */
- for (i = 0; i < handles; i++) {
- u32 handle = tpm_buf_read_u32(buf, &offset_s);
-
- if (auth->name_h[i] != handle) {
- dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
- ret = -EIO;
- goto err;
- }
- }
- /* point offset_s to the start of the sessions */
val = tpm_buf_read_u32(buf, &offset_s);
/* point offset_p to the start of the parameters */
offset_p = offset_s + val;
@@ -682,23 +658,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
/* ordinal is already BE */
sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
/* add the handle names */
- for (i = 0; i < handles; i++) {
- enum tpm2_mso_type mso = tpm2_handle_mso(auth->name_h[i]);
-
- if (mso == TPM2_MSO_PERSISTENT ||
- mso == TPM2_MSO_VOLATILE ||
- mso == TPM2_MSO_NVRAM) {
- ret = name_size(auth->name[i]);
- if (ret < 0)
- goto err;
-
- sha256_update(&sctx, auth->name[i], ret);
- } else {
- __be32 h = cpu_to_be32(auth->name_h[i]);
-
- sha256_update(&sctx, (u8 *)&h, 4);
- }
- }
+ for (i = 0; i < handles; i++)
+ sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
if (offset_s != tpm_buf_length(buf))
sha256_update(&sctx, &buf->data[offset_s],
tpm_buf_length(buf) - offset_s);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index b4b5bd586501..db716841973f 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -539,7 +539,7 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
}
int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name);
+ u32 handle, u8 *name, u16 name_size);
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
u8 attributes, u8 *passphrase,
int passphraselen);
@@ -553,6 +553,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
int rc);
void tpm2_end_auth_session(struct tpm_chip *chip);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
#else
#include <linux/unaligned.h>
@@ -576,6 +577,13 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
{
return rc;
}
+
+static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle,
+ void *name)
+{
+ memcpy(name, &handle, sizeof(u32));
+ return sizeof(u32);
+}
#endif /* CONFIG_TCG_TPM2_HMAC */
#endif
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index fb76c4ea496f..b05808c39d9d 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -233,7 +233,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
+ u8 parent_name[TPM2_MAX_NAME_SIZE];
off_t offset = TPM_HEADER_SIZE;
+ u16 parent_name_size;
int blob_len = 0;
int hash;
u32 flags;
@@ -250,6 +252,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
if (rc)
return rc;
+ rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+ if (rc < 0)
+ goto out_put;
+
+ parent_name_size = rc;
+
rc = tpm2_start_auth_session(chip);
if (rc)
goto out_put;
@@ -264,7 +272,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
- rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+ parent_name_size);
if (rc)
goto out_put;
@@ -356,48 +365,38 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
}
/**
- * tpm2_load_cmd() - execute a TPM2_Load command
- *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: returned blob handle
+ * tpm2_load_cmd() - Execute TPM2_Load
+ * @chip: TPM chip to use.
+ * @payload: Key data in clear text.
+ * @options: Trusted key options.
+ * @parent_name: A cryptographic name, i.e. a TPMT_HA blob, of the
+ * parent key.
+ * @blob: The decoded payload for the key.
+ * @blob_handle: On success, will contain handle to the loaded keyedhash
+ * blob.
*
- * Return: 0 on success.
- * -E2BIG on wrong payload size.
- * -EPERM on tpm error status.
- * < 0 error from tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
*/
static int tpm2_load_cmd(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options,
+ u8 *parent_name,
+ u16 parent_name_size,
+ const u8 *blob,
u32 *blob_handle)
{
u8 *blob_ref __free(kfree) = NULL;
unsigned int private_len;
unsigned int public_len;
unsigned int blob_len;
- u8 *blob, *pub;
+ const u8 *pub;
int rc;
u32 attrs;
- rc = tpm2_key_decode(payload, options, &blob);
- if (rc) {
- /* old form */
- blob = payload->blob;
- payload->old_format = 1;
- } else {
- /* Bind for cleanup: */
- blob_ref = blob;
- }
-
- /* new format carries keyhandle but old format doesn't */
- if (!options->keyhandle)
- return -EINVAL;
-
/* must be big enough for at least the two be16 size counts */
if (payload->blob_len < 4)
- return -EINVAL;
+ return -E2BIG;
private_len = get_unaligned_be16(blob);
@@ -436,7 +435,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
- rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+ parent_name_size);
if (rc)
return rc;
@@ -464,20 +464,23 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
}
/**
- * tpm2_unseal_cmd() - execute a TPM2_Unload command
+ * tpm2_unseal_cmd() - Execute TPM2_Unload
*
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: blob handle
+ * @chip: TPM chip to use
+ * @payload: Key data in clear text.
+ * @options: Trusted key options.
+ * @parent_name: A cryptographic name, i.e. a TPMT_HA blob, of the
+ * parent key.
+ * @blob_handle: Handle to the loaded keyedhash blob.
*
- * Return: 0 on success
- * -EPERM on tpm error status
- * < 0 error from tpm_send
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
*/
static int tpm2_unseal_cmd(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options,
+ u8 *parent_name,
+ u16 parent_name_size,
u32 blob_handle)
{
struct tpm_header *head;
@@ -497,7 +500,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
tpm_buf_init(buf, TPM_BUFSIZE);
tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
- rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+ parent_name_size);
if (rc)
return rc;
@@ -567,30 +571,60 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
}
/**
- * tpm2_unseal_trusted() - unseal the payload of a trusted key
+ * tpm2_unseal_trusted() - Unseal a trusted key
+ * @chip: TPM chip to use.
+ * @payload: Key data in clear text.
+ * @options: Trusted key options.
*
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- *
- * Return: Same as with tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Return -EINVAL when parent's key handle has not been set.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load or TPM2_Unseal
+ * fails.
*/
int tpm2_unseal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
+ u8 *blob_ref __free(kfree) = NULL;
+ u8 parent_name[TPM2_MAX_NAME_SIZE];
+ u16 parent_name_size;
u32 blob_handle;
+ u8 *blob;
int rc;
+ /*
+ * Try to decode the provided blob as an ASN.1 blob. Assume that the
+ * blob is in the legacy format if decoding does not end successfully.
+ */
+ rc = tpm2_key_decode(payload, options, &blob);
+ if (rc) {
+ blob = payload->blob;
+ payload->old_format = 1;
+ } else {
+ blob_ref = blob;
+ }
+
+ if (!options->keyhandle)
+ return -EINVAL;
+
rc = tpm_try_get_ops(chip);
if (rc)
return rc;
- rc = tpm2_load_cmd(chip, payload, options, &blob_handle);
+ rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+ if (rc < 0)
+ goto out;
+
+ parent_name_size = rc;
+
+ rc = tpm2_load_cmd(chip, payload, options, parent_name,
+ parent_name_size, blob, &blob_handle);
if (rc)
goto out;
- rc = tpm2_unseal_cmd(chip, payload, options, blob_handle);
+ rc = tpm2_unseal_cmd(chip, payload, options, parent_name,
+ parent_name_size, blob_handle);
+
tpm2_flush_context(chip, blob_handle);
out:
--
2.39.5
^ permalink raw reply related
* [PATCH v7 1/3] tpm2-sessions: Define TPM2_NAME_MAX_SIZE
From: Jarkko Sakkinen @ 2025-12-08 5:06 UTC (permalink / raw)
To: linux-integrity
Cc: tpm2, Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe,
James Bottomley, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM
In-Reply-To: <20251208050620.339408-1-jarkko@kernel.org>
This is somewhat cosmetic change but it does serve a purpose on tracking
the value set for the maximum length of TPM names, and to clearly states
what components it is composed of. It also anchors the value so that when
buffers are declared for this particular purpose, the same value is used
for the capacity.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v7:
- A new patch.
---
drivers/char/tpm/tpm2-sessions.c | 2 +-
include/linux/tpm.h | 9 +++++++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 79f27a46bd7f..a0c88fb1804c 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -137,7 +137,7 @@ struct tpm2_auth {
* we must compute and remember
*/
u32 name_h[AUTH_MAX_NAMES];
- u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE];
+ u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
};
#ifdef CONFIG_TCG_TPM2_HMAC
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 42e2a091f43d..b4b5bd586501 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -28,7 +28,16 @@
#define TPM_DIGEST_SIZE 20 /* Max TPM v1.2 PCR size */
#define TPM_BUFSIZE 4096
+/*
+ * SHA-512 is, as of today, the largest digest in the TCG algorithm repository.
+ */
#define TPM2_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE
+
+/*
+ * A TPM name digest i.e., TPMT_HA, is a concatenation of TPM_ALG_ID of the
+ * name algorithm and hash of TPMT_PUBLIC.
+ */
+#define TPM2_MAX_NAME_SIZE (TPM2_MAX_DIGEST_SIZE + 2)
#define TPM2_MAX_PCR_BANKS 8
struct tpm_chip;
--
2.39.5
^ permalink raw reply related
* [PATCH v7 0/3] Optimize tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-08 5:06 UTC (permalink / raw)
To: linux-integrity
Cc: tpm2, Jarkko Sakkinen, James Bottomley, Mimi Zohar, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM, open list
The main goal is fairly straight-forwrd here.
The aim of these patches is optimize the number of tpm2_read_public() calls
to the bare minimum.
## About dropping 'parentName' attribute for ASN.1 keys from the patch set
I wrote this section as a remainder as I have facts fresh in my mind so
that I can return them as soon as there is working group for the ASN.1
specification. We really need to have this in the spec.
I dropped [1] given that [2] is landing shortly to IETF draft process,
according to James Bottomley [3]. We will return to [1] as soon as draft
process is open for comments. Still, that attribute is super important,
and here is why.
This will cause a overhead as tpm2_unseal_trusted needs to do an
unnecessary (from pure technical perspective) TPM2_ReadPublic command to
acquire TPM name of the parent. This is obviously known at the time of
creation of a key but the information is not stored anywhere by the
key format.
It also aligns badly with TCG specifications as Table 6 of architecture
spec explicitly defines a reference (or name) for transient keys,
persistent keys and NV indexes to be TPM_ALG_ID concatenated together
with the hash of TPMT_PUBLIC. I.e. the file format is using exactly
the opposite what should be use as reference for keys than what it
should use.
Other benefits are of course auto-discovery of parent for a key file,
which is nasty to do without the name pre-stored.
[1] https://lore.kernel.org/linux-integrity/20251207173210.93765-3-jarkko@kernel.org/
[2] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt
[3] https://lore.kernel.org/linux-integrity/89d90617ba9b7a5eff1d5fad6bb9773033d3c18c.camel@HansenPartnership.com/
Jarkko Sakkinen (3):
tpm2-sessions: Define TPM2_NAME_MAX_SIZE
KEYS: trusted: Re-orchestrate tpm2_read_public() calls
tpm2-sessions: Remove AUTH_MAX_NAMES
drivers/char/tpm/tpm2-cmd.c | 3 +-
drivers/char/tpm/tpm2-sessions.c | 116 ++++++--------------
include/linux/tpm.h | 19 +++-
security/keys/trusted-keys/trusted_tpm2.c | 124 ++++++++++++++--------
4 files changed, 133 insertions(+), 129 deletions(-)
--
2.39.5
^ permalink raw reply
* まるなげ福祉事業
From: 福祉フランチャイズ本部 @ 2025-12-08 1:50 UTC (permalink / raw)
To: linux-security-module
新規事業をご検討中の経営者様へ
いつもお世話になっております。
「社会貢献性の高い事業で、確実な収益を上げたい」
そう考え、成長著しい福祉市場にご関心をお持ちのことと存じます。
しかし、同時にこうも考えていませんか?
「市場は魅力的だが、専門的な法規制や複雑な運営、人材採用は荷が重すぎる…」
「もし失敗したら、多額の投資が無駄になってしまうのでは…」
ご安心ください。その不安こそ、私たちが解決したい最大の課題でした。
この障壁を根本から取り除くのが、私たちtocotocoだけの
【運営本部代行プラン】です。
煩雑な運営業務は全て本部がプロフェッショナルとして代行します。
オーナー様には、「投資家」として市場の確実な成長という
最大の果実のみを受け取っていただきます。
―――――――――――
廃業率0.055% 業態を選べる
障がい福祉フランチャイズ
<ご視聴予約はこちら>
https://fc-tocotoco.work/25/
〇オンラインで開催中
12月9日(火)10:00〜11:00
12月17日(水)15:00〜16:00
ご都合の良い日程をお選びいただけます。
―――――――――――
■ 失敗の不安を解消。本部代行プランの3大メリット
1.専門知識、一切不要
複雑な行政への請求や法改正対応、専門スタッフの採用・管理まで、全て本部が代行します。
異業種出身であることを気にする必要は一切ありません。
2.早期の投資回収を実現
煩雑な運営業務から解放され、オーナー様は事業拡大、
そして投資利回り50%以上、6ヶ月〜での投資回収実績を持つ、
確実な収益構造の構築に専念できます。
3.廃業率0.055%の安定性を最大限享受
業界トップクラスの実績とノウハウを持つ本部が現場を担うため、
「運営不安による失敗」というリスクが限りなくゼロに近づきます。
■ 市場の確実性を数字で証明
・市場規模:4兆円超え
・需要:障がい者数 毎月4万人増加
・安定性:廃業率0.055%(行政による総量規制で事業が守られています)
この巨大かつ安定した市場で、本部が代行することで、
より確実性の高い事業運営が実現します。
【運営代行プラン】は説明会でのみ詳細を公開
投資したいが運営までは荷が重い、という経営者様のために開発されたこの特別プランと、
5つの高収益業態(訪問看護、グループホームなど)の具体的な収益モデルは、
下記の説明会でのみ公開しております。
新規事業のリスクを最小化し、確実な収益源を確保したい方は、
ぜひこの機会にご参加ください。
▼ 詳細はこちら
https://fc-tocotoco.work/25/
―――
tocotoco株式会社 セミナー事務局
東京都千代田区九段南2丁目3−25
03-5256-7578
‥‥‥‥
本メールのご不要な方には大変ご迷惑をおかけいたしました。
メール停止ご希望の方は、お手数ですが下記URLにて、
お手続きをお願いいたします。
https://fc-tocotoco.work/mail/
―――
^ permalink raw reply
* Re: [PATCH v6 2/2] KEYS: trusted: Store parent's name to the encoded keys
From: Jarkko Sakkinen @ 2025-12-07 21:06 UTC (permalink / raw)
To: linux-integrity
Cc: tpm2, Peter Huewe, Jason Gunthorpe, James Bottomley, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list, open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <aTXelmqQ_y7zX-KZ@kernel.org>
On Sun, Dec 07, 2025 at 10:07:55PM +0200, Jarkko Sakkinen wrote:
> On Sun, Dec 07, 2025 at 07:57:13PM +0200, Jarkko Sakkinen wrote:
> > On Sun, Dec 07, 2025 at 07:32:10PM +0200, Jarkko Sakkinen wrote:
> > > Extend TPMKey ASN.1 speciication [1] with an optional 'parentName'
> > > attribute containing TPM name of the parent key (in other words, TPMT_HA
> > > blob).
> > >
> > > The life-cycle for trusted keys will now proceed as follows:
> > >
> > > 1. Encode parent's name to the 'paretName' during tpm2_key_encode().
> > > 2. During tpm2_unseal_trusted, read parent's name from 'parentName'. When
> > > the attribute is not available, fallback on doing tpm2_read_public().
> > >
> > > In other words, in the common (i.e., not loading a legacy key blob),
> > > tpm2_read_public() will now only happen at the time when a key is first
> > > created.
> > >
> > > In addition, move tpm2_read_public() to 'tpm2-cmd.c' and make its body
> > > unconditional so that the binary format of the saved keys is not dependent
> > > on kernel configuration.
> > >
> > > [1] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt
> > >
> > > Cc: tpm2@lists.linux.dev
> > > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> >
> > As an alternative workaround I think the following could be possibly
> > done (I need to trial it first though):
> >
> > 1. Maintain a cache where a name gets added at the time of
> > tpm2_seal_trusted(). It is from TPMT_HA to TPMT_HA mapping,
> > mapping TPMT_HA of the key to the TPMT of the parent.
> > 2. At thet time tpm2_unseal_trusted() retrieve name of thet
> > parent from the cache.
> >
> > Capturing TPM2_CreatePrimary would be essentially duct taping the
> > spec but I guess this could be more generally applicable. It neither
> > addresses persistent keys nor secondary parent keys, which we *have
> > to support*, as the kernel interface does.
>
> I think it is better to focus on merging the first patch and continue
> this in the working groups mailing list first before implementing
> complex quirks to address flaws of the file format.
Storing the name of the parent to the key file has also some other
advantages such as enabling parent auto discovery, which is especially
useful with transient keys. Parent handle is not useful data as by
definition it has an ambiguous meaning.
It is neither "insecure" as child key is tied to one single parent but
at the same parent's handle is completely useless and irrelevant data.
There's nothing "niche" IMHO on storing parent's name at least, and
it is very limiting not to have it.
BR, Jarkko
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox