* Re: [PATCH 1/1] IMA event log trimming
From: Roberto Sassu @ 2025-12-05 14:21 UTC (permalink / raw)
To: 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: <099492ee58996b6f18d73232677757ecadb14cb7.camel@huaweicloud.com>
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)
> - While trimming per se is ok, I like more the idea of staging changes
> and letting the user delete the staged measurements list later
>
> > A mutex ima_trim_list_mutex is provided to allow one trimming request
> > at a time.
> >
> > Signed-off-by: steven chen <chenste@linux.microsoft.com>
> > ---
> > security/integrity/ima/ima.h | 2 +
> > security/integrity/ima/ima_fs.c | 78 ++++++++++++++++++++++++++++++
> > security/integrity/ima/ima_queue.c | 42 ++++++++++++++++
> > 3 files changed, 122 insertions(+)
> >
> > diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> > index e3d71d8d56e3..ab0e30ee25ea 100644
> > --- a/security/integrity/ima/ima.h
> > +++ b/security/integrity/ima/ima.h
> > @@ -246,8 +246,10 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
> >
> > #ifdef CONFIG_IMA_KEXEC
> > void ima_measure_kexec_event(const char *event_name);
> > +long ima_purge_event_log(long number_logs);
> > #else
> > static inline void ima_measure_kexec_event(const char *event_name) {}
> > +static inline long ima_purge_event_log(long number_logs) { return 0; }
> > #endif
> >
> > /*
> > diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> > index 87045b09f120..ea93448feedd 100644
> > --- a/security/integrity/ima/ima_fs.c
> > +++ b/security/integrity/ima/ima_fs.c
> > @@ -38,6 +38,11 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);
> >
> > static int valid_policy = 1;
> >
> > +#define IMA_LOG_TRIM_REQ_LENGTH 11
> > +static long trimcount;
> > +/* mutex protects atomicity of trimming measurement list requests */
> > +static DEFINE_MUTEX(ima_trim_list_mutex);
> > +
> > static ssize_t ima_show_htable_value(char __user *buf, size_t count,
> > loff_t *ppos, atomic_long_t *val)
> > {
> > @@ -289,6 +294,69 @@ static const struct file_operations ima_ascii_measurements_ops = {
> > .release = seq_release,
> > };
> >
> > +static int ima_log_trim_open(struct inode *inode, struct file *filp)
> > +{
> > + if (!capable(CAP_SYS_ADMIN))
> > + return -EPERM;
> > + return 0;
> > +}
> > +
> > +static ssize_t ima_log_trim_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
> > +{
> > + char tmpbuf[IMA_LOG_TRIM_REQ_LENGTH]; /* greater than largest 'long' string value */
> > + ssize_t len;
> > +
> > + len = scnprintf(tmpbuf, sizeof(tmpbuf), "%li\n", trimcount);
> > + return simple_read_from_buffer(buf, size, ppos, tmpbuf, len);
> > +}
> > +
> > +static ssize_t ima_log_trim_write(struct file *file,
> > + const char __user *buf, size_t datalen, loff_t *ppos)
> > +{
> > + unsigned char req[IMA_LOG_TRIM_REQ_LENGTH];
> > + long count, n;
> > + int ret;
> > +
> > + mutex_lock(&ima_trim_list_mutex);
> > +
> > + if (*ppos > 0 || datalen > IMA_LOG_TRIM_REQ_LENGTH || datalen < 2) {
> > + ret = -EINVAL;
> > + goto out;
> > + }
> > +
> > + n = (int)datalen;
> > +
> > + ret = copy_from_user(req, buf, datalen);
> > + if (ret < 0)
> > + goto out;
> > +
> > + count = 0;
> > + for (int i = 0; i < n; ++i) {
> > + if (req[i] < '0' || req[i] > '9') {
> > + ret = -EINVAL;
> > + goto out;
> > + }
> > + count = count * 10 + req[i] - '0';
> > + }
> > + ret = ima_purge_event_log(count);
> > +
> > + if (ret < 0)
> > + goto out;
> > +
> > + trimcount = ret;
> > + ret = datalen;
> > +out:
> > + mutex_unlock(&ima_trim_list_mutex);
> > + return ret;
> > +}
> > +
> > +static const struct file_operations ima_log_trim_ops = {
> > + .open = ima_log_trim_open,
> > + .read = ima_log_trim_read,
> > + .write = ima_log_trim_write,
> > + .llseek = generic_file_llseek,
> > +};
> > +
> > static ssize_t ima_read_policy(char *path)
> > {
> > void *data = NULL;
> > @@ -528,6 +596,16 @@ int __init ima_fs_init(void)
> > goto out;
> > }
> >
> > + dentry = securityfs_create_file("ima_trim_log",
> > + S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP,
> > + ima_dir, NULL, &ima_log_trim_ops);
> > + if (IS_ERR(dentry)) {
> > + ret = PTR_ERR(dentry);
> > + goto out;
> > + }
> > +
> > + trimcount = 0;
> > +
> > 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_queue.c b/security/integrity/ima/ima_queue.c
> > index 590637e81ad1..999cd42c517c 100644
> > --- a/security/integrity/ima/ima_queue.c
> > +++ b/security/integrity/ima/ima_queue.c
> > @@ -220,6 +220,48 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
> > return result;
> > }
> >
> > +/* Delete the IMA event logs */
> > +long ima_purge_event_log(long number_logs)
> > +{
> > + struct ima_queue_entry *qe;
> > + long cur = 0;
> > +
> > + if (number_logs <= 0)
> > + return number_logs;
> > +
> > + mutex_lock(&ima_extend_list_mutex);
> > + rcu_read_lock();
>
> Sorry, I'm missing why rcu_read_lock() is needed.
Ah, I guess it is because of list_for_each_entry_rcu(). In that case, I
think you can add 'true' as 4th argument, since you are holding
ima_extend_list_mutex.
Roberto
> > +
> > + /*
> > + * Remove this entry from both hash table and the measurement list
> > + * When removing from hash table, decrease the length counter
> > + * so that the hash table re-sizing logic works correctly
> > + */
> > + list_for_each_entry_rcu(qe, &ima_measurements, later) {
> > + int i;
> > +
> > + /* if CONFIG_IMA_DISABLE_HTABLE is set, the hash table is not used */
> > + if (!IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE))
> > + hlist_del_rcu(&qe->hnext);
> > +
> > + 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;
> > + }
> > +
> > + atomic_long_dec(&ima_htable.len);
> > + list_del_rcu(&qe->later);
>
> Missing kfree() of qe->entry and qe?
>
> Thanks
>
> Roberto
>
> > + ++cur;
> > + if (cur >= number_logs)
> > + break;
> > + }
> > +
> > + rcu_read_unlock();
> > + mutex_unlock(&ima_extend_list_mutex);
> > + return cur;
> > +}
> > +
> > int ima_restore_measurement_entry(struct ima_template_entry *entry)
> > {
> > int result = 0;
^ permalink raw reply
* Island: Sandboxing tool powered by Landlock
From: Mickaël Salaün @ 2025-12-05 11:22 UTC (permalink / raw)
To: landlock; +Cc: oss-security, linux-security-module, lwn
Hi,
I just released Island, a sandboxing tool powered by Landlock:
https://github.com/landlock-lsm/island
Island makes Landlock practical for everyday workflows by acting as a
high-level wrapper and policy manager. Developed alongside the kernel
feature and its Rust libraries, it bridges the gap between raw security
mechanisms and user activity through:
- Zero-code integration: Runs existing binaries without modification.
- Declarative policies: Uses TOML profiles instead of code-based rules.
- Context-aware activation: Automatically applies security profiles
based on your current working directory.
- Full environment isolation: Manages isolated workspaces (XDG
directories, TMPDIR) in addition to access control.
- Transparent shell integration: Automatically sandboxes commands in
your shell without changing your workflow.
- Zero-privilege operation: No root access or special capabilities
required.
- Layered protection: Multiple profiles compose cleanly with
deterministic ordering.
It's a work in progress, so be careful.
Feedback welcome!
Regards,
Mickaël
^ permalink raw reply
* Re: [PATCH] fuse: fix conversion of fuse_reverse_inval_entry() to start_removing()
From: Christian Brauner @ 2025-12-05 13:09 UTC (permalink / raw)
To: Miklos Szeredi
Cc: Al Viro, Amir Goldstein, NeilBrown, Val Packett, Jan Kara,
linux-fsdevel, Jeff Layton, Chris Mason, David Sterba,
David Howells, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Tyler Hicks, Chuck Lever, Olga Kornievskaia,
Dai Ngo, Namjae Jeon, Steve French, Sergey Senozhatsky,
Carlos Maiolino, John Johansen, Paul Moore, James Morris,
Serge E. Hallyn, Stephen Smalley, Ondrej Mosnacek, Mateusz Guzik,
Lorenzo Stoakes, Stefan Berger, Darrick J. Wong, linux-kernel,
netfs, ecryptfs, linux-nfs, linux-unionfs, linux-cifs, linux-xfs,
linux-security-module, selinux
In-Reply-To: <CAJfpegs+o01jgY76WsGnk9j41LS5V0JQSk--d6xsJJp4VjTh8Q@mail.gmail.com>
On Mon, Dec 01, 2025 at 03:03:08PM +0100, Miklos Szeredi wrote:
> On Mon, 1 Dec 2025 at 09:33, Al Viro <viro@zeniv.linux.org.uk> wrote:
> >
> > On Mon, Dec 01, 2025 at 09:22:54AM +0100, Amir Goldstein wrote:
> >
> > > I don't think there is a point in optimizing parallel dir operations
> > > with FUSE server cache invalidation, but maybe I am missing
> > > something.
> >
> > The interesting part is the expected semantics of operation;
> > d_invalidate() side definitely doesn't need any of that cruft,
> > but I would really like to understand what that function
> > is supposed to do.
> >
> > Miklos, could you post a brain dump on that?
>
> This function is supposed to invalidate a dentry due to remote changes
> (FUSE_NOTIFY_INVAL_ENTRY). Originally it was supplied a parent ID and
> a name and called d_invalidate() on the looked up dentry.
>
> Then it grew a variant (FUSE_NOTIFY_DELETE) that was also supplied a
> child ID, which was matched against the looked up inode. This was
> commit 451d0f599934 ("FUSE: Notifying the kernel of deletion."),
> Apparently this worked around the fact that at that time
> d_invalidate() returned -EBUSY if the target was still in use and
> didn't unhash the dentry in that case.
>
> That was later changed by commit bafc9b754f75 ("vfs: More precise
> tests in d_invalidate") to unconditionally unhash the target, which
> effectively made FUSE_NOTIFY_INVAL_ENTRY and FUSE_NOTIFY_DELETE
> equivalent and the code in question unnecessary.
>
> For the future, we could also introduce FUSE_NOTIFY_MOVE, that would
> differentiate between a delete and a move, while
> FUSE_NOTIFY_INVAL_ENTRY would continue to be the common (deleted or
> moved) notification.
>
> Attaching untested patch to remove this cruft.
Should we revert the fuse specific bits of c9ba789dad15 ("VFS: introduce
start_creating_noperm() and start_removing_noperm()") and then apply
your changes afterwards?
^ permalink raw reply
* Re: [oss-security] Island: Sandboxing tool powered by Landlock
From: kf503bla @ 2025-12-05 12:35 UTC (permalink / raw)
To: oss-security@lists.openwall.com
Cc: landlock@lists.linux.dev, linux-security-module@vger.kernel.org,
lwn@lwn.net
In-Reply-To: <303DA753-37EF-4530-AA63-4C3579E9A454.1@smtp-inbound1.duck.com>
Why would you publish it on github? Please consider codeberg or src hut.
Sent with Proton Mail secure email.
On Friday, December 5th, 2025 at 5:14 PM, Mickaël Salaün <mic_at_digikod.net_kf503bla@duck.com> wrote:
>
>
> Hi,
>
> I just released Island, a sandboxing tool powered by Landlock:
> https://github.com/landlock-lsm/island
>
> Island makes Landlock practical for everyday workflows by acting as a
> high-level wrapper and policy manager. Developed alongside the kernel
> feature and its Rust libraries, it bridges the gap between raw security
> mechanisms and user activity through:
> - Zero-code integration: Runs existing binaries without modification.
> - Declarative policies: Uses TOML profiles instead of code-based rules.
> - Context-aware activation: Automatically applies security profiles
> based on your current working directory.
> - Full environment isolation: Manages isolated workspaces (XDG
> directories, TMPDIR) in addition to access control.
> - Transparent shell integration: Automatically sandboxes commands in
> your shell without changing your workflow.
> - Zero-privilege operation: No root access or special capabilities
> required.
> - Layered protection: Multiple profiles compose cleanly with
> deterministic ordering.
>
> It's a work in progress, so be careful.
>
> Feedback welcome!
>
> Regards,
> Mickaël
^ permalink raw reply
* Re: [PATCH 1/1] IMA event log trimming
From: Roberto Sassu @ 2025-12-05 9:30 UTC (permalink / raw)
To: 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: <20251202232857.8211-2-chenste@linux.microsoft.com>
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)
- While trimming per se is ok, I like more the idea of staging changes
and letting the user delete the staged measurements list later
> A mutex ima_trim_list_mutex is provided to allow one trimming request
> at a time.
>
> Signed-off-by: steven chen <chenste@linux.microsoft.com>
> ---
> security/integrity/ima/ima.h | 2 +
> security/integrity/ima/ima_fs.c | 78 ++++++++++++++++++++++++++++++
> security/integrity/ima/ima_queue.c | 42 ++++++++++++++++
> 3 files changed, 122 insertions(+)
>
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index e3d71d8d56e3..ab0e30ee25ea 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -246,8 +246,10 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
>
> #ifdef CONFIG_IMA_KEXEC
> void ima_measure_kexec_event(const char *event_name);
> +long ima_purge_event_log(long number_logs);
> #else
> static inline void ima_measure_kexec_event(const char *event_name) {}
> +static inline long ima_purge_event_log(long number_logs) { return 0; }
> #endif
>
> /*
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 87045b09f120..ea93448feedd 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -38,6 +38,11 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);
>
> static int valid_policy = 1;
>
> +#define IMA_LOG_TRIM_REQ_LENGTH 11
> +static long trimcount;
> +/* mutex protects atomicity of trimming measurement list requests */
> +static DEFINE_MUTEX(ima_trim_list_mutex);
> +
> static ssize_t ima_show_htable_value(char __user *buf, size_t count,
> loff_t *ppos, atomic_long_t *val)
> {
> @@ -289,6 +294,69 @@ static const struct file_operations ima_ascii_measurements_ops = {
> .release = seq_release,
> };
>
> +static int ima_log_trim_open(struct inode *inode, struct file *filp)
> +{
> + if (!capable(CAP_SYS_ADMIN))
> + return -EPERM;
> + return 0;
> +}
> +
> +static ssize_t ima_log_trim_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
> +{
> + char tmpbuf[IMA_LOG_TRIM_REQ_LENGTH]; /* greater than largest 'long' string value */
> + ssize_t len;
> +
> + len = scnprintf(tmpbuf, sizeof(tmpbuf), "%li\n", trimcount);
> + return simple_read_from_buffer(buf, size, ppos, tmpbuf, len);
> +}
> +
> +static ssize_t ima_log_trim_write(struct file *file,
> + const char __user *buf, size_t datalen, loff_t *ppos)
> +{
> + unsigned char req[IMA_LOG_TRIM_REQ_LENGTH];
> + long count, n;
> + int ret;
> +
> + mutex_lock(&ima_trim_list_mutex);
> +
> + if (*ppos > 0 || datalen > IMA_LOG_TRIM_REQ_LENGTH || datalen < 2) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + n = (int)datalen;
> +
> + ret = copy_from_user(req, buf, datalen);
> + if (ret < 0)
> + goto out;
> +
> + count = 0;
> + for (int i = 0; i < n; ++i) {
> + if (req[i] < '0' || req[i] > '9') {
> + ret = -EINVAL;
> + goto out;
> + }
> + count = count * 10 + req[i] - '0';
> + }
> + ret = ima_purge_event_log(count);
> +
> + if (ret < 0)
> + goto out;
> +
> + trimcount = ret;
> + ret = datalen;
> +out:
> + mutex_unlock(&ima_trim_list_mutex);
> + return ret;
> +}
> +
> +static const struct file_operations ima_log_trim_ops = {
> + .open = ima_log_trim_open,
> + .read = ima_log_trim_read,
> + .write = ima_log_trim_write,
> + .llseek = generic_file_llseek,
> +};
> +
> static ssize_t ima_read_policy(char *path)
> {
> void *data = NULL;
> @@ -528,6 +596,16 @@ int __init ima_fs_init(void)
> goto out;
> }
>
> + dentry = securityfs_create_file("ima_trim_log",
> + S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP,
> + ima_dir, NULL, &ima_log_trim_ops);
> + if (IS_ERR(dentry)) {
> + ret = PTR_ERR(dentry);
> + goto out;
> + }
> +
> + trimcount = 0;
> +
> 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_queue.c b/security/integrity/ima/ima_queue.c
> index 590637e81ad1..999cd42c517c 100644
> --- a/security/integrity/ima/ima_queue.c
> +++ b/security/integrity/ima/ima_queue.c
> @@ -220,6 +220,48 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
> return result;
> }
>
> +/* Delete the IMA event logs */
> +long ima_purge_event_log(long number_logs)
> +{
> + struct ima_queue_entry *qe;
> + long cur = 0;
> +
> + if (number_logs <= 0)
> + return number_logs;
> +
> + mutex_lock(&ima_extend_list_mutex);
> + rcu_read_lock();
Sorry, I'm missing why rcu_read_lock() is needed.
> +
> + /*
> + * Remove this entry from both hash table and the measurement list
> + * When removing from hash table, decrease the length counter
> + * so that the hash table re-sizing logic works correctly
> + */
> + list_for_each_entry_rcu(qe, &ima_measurements, later) {
> + int i;
> +
> + /* if CONFIG_IMA_DISABLE_HTABLE is set, the hash table is not used */
> + if (!IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE))
> + hlist_del_rcu(&qe->hnext);
> +
> + 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;
> + }
> +
> + atomic_long_dec(&ima_htable.len);
> + list_del_rcu(&qe->later);
Missing kfree() of qe->entry and qe?
Thanks
Roberto
> + ++cur;
> + if (cur >= number_logs)
> + break;
> + }
> +
> + rcu_read_unlock();
> + mutex_unlock(&ima_extend_list_mutex);
> + return cur;
> +}
> +
> int ima_restore_measurement_entry(struct ima_template_entry *entry)
> {
> int result = 0;
^ permalink raw reply
* Re: [GIT PULL] capabilities update for v6.19
From: Serge E. Hallyn @ 2025-12-05 4:43 UTC (permalink / raw)
To: Linus Torvalds
Cc: Serge E. Hallyn, linux-security-module, Linux Kernel Mailing List,
Ryan Foster, Christian Brauner
In-Reply-To: <CAADWXX_rO42NznU6c+rjqzaUuTLMp_DXSf_mn8rVDCe+1AMUTQ@mail.gmail.com>
On Thu, Dec 04, 2025 at 05:20:18PM -0800, Linus Torvalds wrote:
> On Thu, Dec 4, 2025 at 4:27 PM Serge E. Hallyn <serge@hallyn.com> wrote:
> >
> > This contains the capabilities changes for 6.19.
>
> This email was in my spam folder.
>
> The reason appears to be due to it being marked as "DMARC: FAIL".
>
> I think it's simply because your mail domain doesn't have a dmarc
> policy at all, and gmail has been getting more strict about having
> both dkim and dmarc set up.
>
> Anyway, I obviously found the email, but I thought I'd mention it,
> because while I check my spam folder fairly religiously, it's still
> mostly a pretty quick scan for possible emails. There's no guarantee I
> always notice...
>
> Linus
Thanks! I set up SPF years ago, but guess I will have to figure out
DKIM.
The odd thing is, when I send emails to my own gmail account I don't see
any complaints about DKIM or DMARC, only:
ARC-Authentication-Results: i=1; mx.google.com;
spf=pass (google.com: domain of serge@hallyn.com designates 178.63.66.53 as permitted sender) smtp.mailfrom=serge@hallyn.com
But I'll fix it.
thanks,
-serge
^ permalink raw reply
* Re: [GIT PULL] capabilities update for v6.19
From: pr-tracker-bot @ 2025-12-05 4:15 UTC (permalink / raw)
To: Serge E. Hallyn
Cc: torvalds, linux-security-module, Linux Kernel Mailing List,
Ryan Foster, Christian Brauner
In-Reply-To: <aTIm6grHZUhRncFH@mail.hallyn.com>
The pull request you sent on Thu, 4 Dec 2025 18:27:22 -0600:
> https://git.kernel.org/pub/scm/linux/kernel/git/sergeh/linux.git tags/caps-pr-20251204
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/2061f18ad76ecaddf8ed17df81b8611ea88dbddd
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
* [PATCH v3 2/2] KEYS: trusted: Store parent's name to the encoded keys
From: Jarkko Sakkinen @ 2025-12-05 4:00 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: <20251205040021.207369-1-jarkko@kernel.org>
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
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v3:
- Fixed three low-hanging fruit issues I found myself.
v2:
- A new patch.
---
drivers/char/tpm/tpm2-cmd.c | 90 ++++++++++++++++++++++
drivers/char/tpm/tpm2-sessions.c | 92 ----------------------
include/linux/tpm.h | 9 +--
security/keys/trusted-keys/tpm2key.asn1 | 17 ++++-
security/keys/trusted-keys/trusted_tpm2.c | 93 ++++++++++++++++-------
5 files changed, 173 insertions(+), 128 deletions(-)
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 1f561ad3bdcf..360bc014d895 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -793,3 +793,93 @@ int tpm2_find_cc(struct tpm_chip *chip, u32 cc)
return -1;
}
+
+/*
+ * Name Size based on TPM algorithm (assumes no hash bigger than 255)
+ */
+static int name_size(const u8 *name)
+{
+ u16 hash_alg = get_unaligned_be16(name);
+
+ switch (hash_alg) {
+ case TPM_ALG_SHA1:
+ return SHA1_DIGEST_SIZE + 2;
+ case TPM_ALG_SHA256:
+ return SHA256_DIGEST_SIZE + 2;
+ case TPM_ALG_SHA384:
+ return SHA384_DIGEST_SIZE + 2;
+ case TPM_ALG_SHA512:
+ return SHA512_DIGEST_SIZE + 2;
+ default:
+ pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg);
+ return -EINVAL;
+ }
+}
+
+/**
+ * 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;
+ int rc, name_size_alg;
+ struct tpm_buf buf;
+
+ if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
+ mso != TPM2_MSO_NVRAM) {
+ memcpy(name, &handle, sizeof(u32));
+ return sizeof(u32);
+ }
+
+ rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
+ if (rc)
+ return rc;
+
+ tpm_buf_append_u32(&buf, handle);
+
+ rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic");
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ return tpm_ret_to_err(rc);
+ }
+
+ /* Skip TPMT_PUBLIC: */
+ offset += tpm_buf_read_u16(&buf, &offset);
+
+ /*
+ * Ensure space for the length field of TPM2B_NAME and hashAlg field of
+ * TPMT_HA (the extra four bytes).
+ */
+ if (offset + 4 > tpm_buf_length(&buf)) {
+ tpm_buf_destroy(&buf);
+ return -EIO;
+ }
+
+ rc = tpm_buf_read_u16(&buf, &offset);
+ name_size_alg = name_size(&buf.data[offset]);
+
+ if (name_size_alg < 0)
+ return name_size_alg;
+
+ if (rc != name_size_alg) {
+ tpm_buf_destroy(&buf);
+ return -EIO;
+ }
+
+ if (offset + rc > tpm_buf_length(&buf)) {
+ tpm_buf_destroy(&buf);
+ return -EIO;
+ }
+
+ memcpy(name, &buf.data[offset], rc);
+ return name_size_alg;
+}
+EXPORT_SYMBOL_GPL(tpm2_read_public);
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index e33be09446ff..c8b44b1a6cb6 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -140,98 +140,6 @@ struct tpm2_auth {
u16 name_size_tbl[AUTH_MAX_NAMES];
};
-#ifdef CONFIG_TCG_TPM2_HMAC
-/*
- * Name Size based on TPM algorithm (assumes no hash bigger than 255)
- */
-static int name_size(const u8 *name)
-{
- u16 hash_alg = get_unaligned_be16(name);
-
- switch (hash_alg) {
- case TPM_ALG_SHA1:
- return SHA1_DIGEST_SIZE + 2;
- case TPM_ALG_SHA256:
- return SHA256_DIGEST_SIZE + 2;
- case TPM_ALG_SHA384:
- return SHA384_DIGEST_SIZE + 2;
- case TPM_ALG_SHA512:
- return SHA512_DIGEST_SIZE + 2;
- default:
- pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg);
- return -EINVAL;
- }
-}
-
-/**
- * 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;
- int rc, name_size_alg;
- struct tpm_buf buf;
-
- if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
- mso != TPM2_MSO_NVRAM) {
- memcpy(name, &handle, sizeof(u32));
- return sizeof(u32);
- }
-
- rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
- if (rc)
- return rc;
-
- tpm_buf_append_u32(&buf, handle);
-
- rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic");
- if (rc) {
- tpm_buf_destroy(&buf);
- return tpm_ret_to_err(rc);
- }
-
- /* Skip TPMT_PUBLIC: */
- offset += tpm_buf_read_u16(&buf, &offset);
-
- /*
- * Ensure space for the length field of TPM2B_NAME and hashAlg field of
- * TPMT_HA (the extra four bytes).
- */
- if (offset + 4 > tpm_buf_length(&buf)) {
- tpm_buf_destroy(&buf);
- return -EIO;
- }
-
- rc = tpm_buf_read_u16(&buf, &offset);
- name_size_alg = name_size(&buf.data[offset]);
-
- if (name_size_alg < 0)
- return name_size_alg;
-
- if (rc != name_size_alg) {
- tpm_buf_destroy(&buf);
- return -EIO;
- }
-
- if (offset + rc > tpm_buf_length(&buf)) {
- tpm_buf_destroy(&buf);
- return -EIO;
- }
-
- 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() - Append a handle and store TPM name
* @chip: TPM chip to use.
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 319ba75dd79a..704fceb46a40 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -478,6 +478,7 @@ extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max);
extern struct tpm_chip *tpm_default_chip(void);
void tpm2_flush_context(struct tpm_chip *chip, u32 handle);
int tpm2_find_hash_alg(unsigned int crypto_id);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle)
{
@@ -544,7 +545,6 @@ 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>
@@ -568,13 +568,6 @@ 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/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
index f57f869ad600..70cca3b7c3d4 100644
--- a/security/keys/trusted-keys/tpm2key.asn1
+++ b/security/keys/trusted-keys/tpm2key.asn1
@@ -1,11 +1,26 @@
---
--- ASN.1 for TPM 2.0 keys
---
+TPMPolicy ::= SEQUENCE {
+ commandCode [0] EXPLICIT INTEGER,
+ commandPolicy [1] EXPLICIT OCTET STRING
+}
+
+TPMAuthPolicy ::= SEQUENCE {
+ name [0] EXPLICIT UTF8String OPTIONAL,
+ policy [1] EXPLICIT SEQUENCE OF TPMPolicy
+}
TPMKey ::= SEQUENCE {
type OBJECT IDENTIFIER ({tpm2_key_type}),
emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL,
+ policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL,
+ secret [2] EXPLICIT OCTET STRING OPTIONAL,
+ authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL,
+ description [4] EXPLICIT UTF8String OPTIONAL,
+ rsaParent [5] EXPLICIT BOOLEAN OPTIONAL,
+ parentName [6] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_parent_name}),
parent INTEGER ({tpm2_key_parent}),
pubkey OCTET STRING ({tpm2_key_pub}),
privkey OCTET STRING ({tpm2_key_priv})
- }
+}
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 88bafbcc011a..07f04cc010bc 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -20,16 +20,26 @@
static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 };
+enum tpm_key_tag {
+ TPM_KEY_TAG_EMPTY_AUTH = 0,
+ TPM_KEY_TAG_POLICY = 1,
+ TPM_KEY_TAG_SECRET = 2,
+ TPM_KEY_TAG_AUTH_POLICY = 3,
+ TPM_KEY_TAG_DESCRIPTION = 4,
+ TPM_KEY_TAG_RSA_PARENT = 5,
+ TPM_KEY_TAG_PARENT_NAME = 6,
+};
+
static int tpm2_key_encode(struct trusted_key_payload *payload,
struct trusted_key_options *options,
- u8 *src, u32 len)
+ u8 *src, u32 len, u8 *parent_name,
+ u16 parent_name_size)
{
const int SCRATCH_SIZE = PAGE_SIZE;
- u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
- u8 *work = scratch, *work1;
- u8 *end_work = scratch + SCRATCH_SIZE;
- u8 *priv, *pub;
+ u8 *end_work, *name_end;
u16 priv_len, pub_len;
+ u8 *work, *work1;
+ u8 *priv, *pub;
int ret;
priv_len = get_unaligned_be16(src) + 2;
@@ -40,23 +50,41 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
pub_len = get_unaligned_be16(src) + 2;
pub = src;
+ u8 *scratch __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
if (!scratch)
return -ENOMEM;
+ work = scratch;
+ end_work = scratch + SCRATCH_SIZE;
+
work = asn1_encode_oid(work, end_work, tpm2key_oid,
asn1_oid_len(tpm2key_oid));
if (options->blobauth_len == 0) {
- unsigned char bool[3], *w = bool;
- /* tag 0 is emptyAuth */
- w = asn1_encode_boolean(w, w + sizeof(bool), true);
- if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) {
- ret = PTR_ERR(w);
- goto err;
+ u8 *bool_end;
+ u8 bool[3];
+
+ bool_end = asn1_encode_boolean(&bool[0], &bool[sizeof(bool)],
+ true);
+ if (IS_ERR(bool_end)) {
+ pr_err("BUG: Boolean failed to encode\n");
+ return PTR_ERR(bool_end);
}
- work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
+
+ work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_EMPTY_AUTH,
+ bool, bool_end - bool);
}
+ u8 *name_encoded __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
+ if (!name_encoded)
+ return -ENOMEM;
+
+ name_end = asn1_encode_octet_string(&name_encoded[0],
+ &name_encoded[SCRATCH_SIZE],
+ parent_name, parent_name_size);
+ work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_PARENT_NAME,
+ name_encoded, name_end - name_encoded);
+
/*
* Assume both octet strings will encode to a 2 byte definite length
*
@@ -65,8 +93,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
*/
if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
"BUG: scratch buffer is too small")) {
- ret = -EINVAL;
- goto err;
+ return -EINVAL;
}
work = asn1_encode_integer(work, end_work, options->keyhandle);
@@ -79,15 +106,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
if (IS_ERR(work1)) {
ret = PTR_ERR(work1);
pr_err("BUG: ASN.1 encoder failed with %d\n", ret);
- goto err;
+ return ret;
}
- kfree(scratch);
return work1 - payload->blob;
-
-err:
- kfree(scratch);
- return ret;
}
struct tpm2_key_context {
@@ -96,11 +118,13 @@ struct tpm2_key_context {
u32 pub_len;
const u8 *priv;
u32 priv_len;
+ const u8 *name;
+ u32 name_len;
};
static int tpm2_key_decode(struct trusted_key_payload *payload,
struct trusted_key_options *options,
- u8 **buf)
+ u8 **buf, u8 *parent_name, u16 *parent_name_size)
{
int ret;
struct tpm2_key_context ctx;
@@ -127,6 +151,8 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
blob += ctx.priv_len;
memcpy(blob, ctx.pub, ctx.pub_len);
+ memcpy(parent_name, ctx.name, ctx.name_len);
+ *parent_name_size = ctx.name_len;
return 0;
}
@@ -190,6 +216,16 @@ int tpm2_key_priv(void *context, size_t hdrlen,
return 0;
}
+int tpm2_key_parent_name(void *context, size_t hdrlen, unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct tpm2_key_context *ctx = context;
+
+ ctx->name = value;
+ ctx->name_len = vlen;
+
+ return 0;
+}
/**
* tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
*
@@ -347,7 +383,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out;
}
- blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len);
+ blob_len = tpm2_key_encode(payload, options, &buf.data[offset],
+ blob_len, parent_name, parent_name_size);
if (blob_len < 0)
rc = blob_len;
@@ -602,7 +639,7 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
* 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);
+ rc = tpm2_key_decode(payload, options, &blob, &parent_name[0], &parent_name_size);
if (rc) {
blob = payload->blob;
payload->old_format = 1;
@@ -617,11 +654,13 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
if (rc)
return rc;
- rc = tpm2_read_public(chip, options->keyhandle, parent_name);
- if (rc < 0)
- goto out;
+ if (!parent_name_size) {
+ rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+ if (rc < 0)
+ goto out;
- parent_name_size = rc;
+ parent_name_size = rc;
+ }
rc = tpm2_load_cmd(chip, payload, options, parent_name,
parent_name_size, blob, &blob_handle);
--
2.52.0
^ permalink raw reply related
* [PATCH v3 1/2] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-05 4:00 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: <20251205040021.207369-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>
---
v3:
- No changes.
v2:
- No changes.
---
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 3a77be7ebf4a..1f561ad3bdcf 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -202,7 +202,8 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
}
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) {
tpm_buf_destroy(&buf);
return rc;
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 4149379665c4..e33be09446ff 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][2 + SHA512_DIGEST_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;
@@ -219,14 +229,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
@@ -243,15 +255,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
@@ -276,36 +287,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);
@@ -613,22 +603,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;
@@ -689,23 +665,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 202da079d500..319ba75dd79a 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -530,7 +530,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);
@@ -544,6 +544,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>
@@ -567,6 +568,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 a7ea4a1c3bed..88bafbcc011a 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -233,8 +233,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
+ u8 parent_name[2 + SHA512_DIGEST_SIZE];
off_t offset = TPM_HEADER_SIZE;
struct tpm_buf buf, sized;
+ u16 parent_name_size;
int blob_len = 0;
int hash;
u32 flags;
@@ -251,6 +253,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;
@@ -268,7 +276,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out_put;
}
- 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;
@@ -355,21 +364,25 @@ 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;
@@ -377,27 +390,13 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
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);
@@ -433,7 +432,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
return rc;
}
- 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;
@@ -465,20 +465,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;
@@ -498,7 +501,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
return rc;
}
- 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;
@@ -573,30 +577,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[2 + SHA512_DIGEST_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 v2 2/2] KEYS: trusted: Store parent's name to the encoded keys
From: Jarkko Sakkinen @ 2025-12-05 3:14 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: <20251205030205.140842-3-jarkko@kernel.org>
On Fri, Dec 05, 2025 at 05:02:05AM +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.
>
> [1] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt
>
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> ---
> v2:
> - A new patch.
> ---
> security/keys/trusted-keys/tpm2key.asn1 | 17 +++-
> security/keys/trusted-keys/trusted_tpm2.c | 97 ++++++++++++++++-------
> 2 files changed, 84 insertions(+), 30 deletions(-)
>
> diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
> index f57f869ad600..080f0e399982 100644
> --- a/security/keys/trusted-keys/tpm2key.asn1
> +++ b/security/keys/trusted-keys/tpm2key.asn1
> @@ -1,11 +1,26 @@
> ---
> --- ASN.1 for TPM 2.0 keys
> ---
> +TPMPolicy ::= SEQUENCE {
> + commandCode [0] EXPLICIT INTEGER,
> + commandPolicy [1] EXPLICIT OCTET STRING
> +}
> +
> +TPMAuthPolicy ::= SEQUENCE {
> + name [0] EXPLICIT UTF8String OPTIONAL,
> + policy [1] EXPLICIT SEQUENCE OF TPMPolicy
> +}
>
> TPMKey ::= SEQUENCE {
> type OBJECT IDENTIFIER ({tpm2_key_type}),
> emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL,
> + policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL,
> + secret [2] EXPLICIT OCTET STRING OPTIONAL,
> + authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL,
> + description [4] EXPLICIT UTF8String OPTIONAL,
> + rsaParent [5] EXPLICIT BOOLEAN OPTIONAL,
> + parentName [6] EXPLICIT OCTET STRING ({tpm2_key_parent_name}),
Another nit. Should probably be instead:
parentName [6] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_parent_name}),
> parent INTEGER ({tpm2_key_parent}),
> pubkey OCTET STRING ({tpm2_key_pub}),
> privkey OCTET STRING ({tpm2_key_priv})
> - }
> +}
> diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> index 88bafbcc011a..3608dc9f7fa4 100644
> --- a/security/keys/trusted-keys/trusted_tpm2.c
> +++ b/security/keys/trusted-keys/trusted_tpm2.c
> @@ -20,16 +20,26 @@
>
> static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 };
>
> +enum tpm_key_tag {
> + TPM_KEY_TAG_EMPTY_AUTH = 0,
> + TPM_KEY_TAG_POLICY = 1,
> + TPM_KEY_TAG_SECRET = 2,
> + TPM_KEY_TAG_AUTH_POLICY = 3,
> + TPM_KEY_TAG_DESCRIPTION = 4,
> + TPM_KEY_TAG_RSA_PARENT = 5,
> + TPM_KEY_TAG_PARENT_NAME = 6,
> +};
> +
> static int tpm2_key_encode(struct trusted_key_payload *payload,
> struct trusted_key_options *options,
> - u8 *src, u32 len)
> + u8 *src, u32 len, u8 *parent_name,
> + u16 parent_name_size)
> {
> const int SCRATCH_SIZE = PAGE_SIZE;
> - u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
> - u8 *work = scratch, *work1;
> - u8 *end_work = scratch + SCRATCH_SIZE;
> - u8 *priv, *pub;
> + u8 *end_work, *name_end;
> u16 priv_len, pub_len;
> + u8 *work, *work1;
> + u8 *priv, *pub;
> int ret;
>
> priv_len = get_unaligned_be16(src) + 2;
> @@ -40,23 +50,41 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
> pub_len = get_unaligned_be16(src) + 2;
> pub = src;
>
> + u8 *scratch __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
> if (!scratch)
> return -ENOMEM;
>
> + work = scratch;
> + end_work = scratch + SCRATCH_SIZE;
> +
> work = asn1_encode_oid(work, end_work, tpm2key_oid,
> asn1_oid_len(tpm2key_oid));
>
> if (options->blobauth_len == 0) {
> - unsigned char bool[3], *w = bool;
> - /* tag 0 is emptyAuth */
> - w = asn1_encode_boolean(w, w + sizeof(bool), true);
> - if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) {
> - ret = PTR_ERR(w);
> - goto err;
> + u8 *bool_end;
> + u8 bool[3];
> +
> + bool_end = asn1_encode_boolean(&bool[0], &bool[sizeof(bool)],
> + true);
> + if (IS_ERR(bool_end)) {
> + pr_err("BUG: Boolean failed to encode\n");
> + return PTR_ERR(bool_end);
> }
> - work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
> +
> + work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_EMPTY_AUTH,
> + bool, bool_end - bool);
> }
>
> + u8 *name_encoded __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
> + if (!name_encoded)
> + return -ENOMEM;
> +
> + name_end = asn1_encode_octet_string(&name_encoded[0],
> + &name_encoded[SCRATCH_SIZE],
> + parent_name, parent_name_size);
> + work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_PARENT_NAME,
> + name_encoded, name_end - name_encoded);
> +
> /*
> * Assume both octet strings will encode to a 2 byte definite length
> *
> @@ -65,8 +93,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
> */
> if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
> "BUG: scratch buffer is too small")) {
> - ret = -EINVAL;
> - goto err;
> + return -EINVAL;
> }
>
> work = asn1_encode_integer(work, end_work, options->keyhandle);
> @@ -79,15 +106,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
> if (IS_ERR(work1)) {
> ret = PTR_ERR(work1);
> pr_err("BUG: ASN.1 encoder failed with %d\n", ret);
> - goto err;
> + return ret;
> }
>
> - kfree(scratch);
> return work1 - payload->blob;
> -
> -err:
> - kfree(scratch);
> - return ret;
> }
>
> struct tpm2_key_context {
> @@ -96,11 +118,13 @@ struct tpm2_key_context {
> u32 pub_len;
> const u8 *priv;
> u32 priv_len;
> + const u8 *name;
> + u32 name_len;
> };
>
> static int tpm2_key_decode(struct trusted_key_payload *payload,
> struct trusted_key_options *options,
> - u8 **buf)
> + u8 **buf, u8 *parent_name, u16 *parent_name_size)
> {
> int ret;
> struct tpm2_key_context ctx;
> @@ -127,6 +151,8 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
> blob += ctx.priv_len;
>
> memcpy(blob, ctx.pub, ctx.pub_len);
> + memcpy(parent_name, ctx.name, ctx.name_len);
> + *parent_name_size = ctx.name_len;
>
> return 0;
> }
> @@ -190,6 +216,16 @@ int tpm2_key_priv(void *context, size_t hdrlen,
> return 0;
> }
>
> +int tpm2_key_parent_name(void *context, size_t hdrlen, unsigned char tag,
> + const void *value, size_t vlen)
> +{
> + struct tpm2_key_context *ctx = context;
> +
> + ctx->name = value;
> + ctx->name_len = vlen;
> +
> + return 0;
> +}
> /**
> * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
> *
> @@ -347,7 +383,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> goto out;
> }
>
> - blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len);
> + blob_len = tpm2_key_encode(payload, options, &buf.data[offset],
> + blob_len, parent_name, parent_name_size);
> if (blob_len < 0)
> rc = blob_len;
>
> @@ -602,7 +639,7 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
> * 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);
> + rc = tpm2_key_decode(payload, options, &blob, &parent_name[0], &parent_name_size);
> if (rc) {
> blob = payload->blob;
> payload->old_format = 1;
> @@ -613,16 +650,18 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
> if (!options->keyhandle)
> return -EINVAL;
>
> + if (!parent_name_size) {
> + rc = tpm2_read_public(chip, options->keyhandle, parent_name);
> + if (rc < 0)
> + goto out;
> +
> + parent_name_size = rc;
> + }
> +
> rc = tpm_try_get_ops(chip);
> if (rc)
> return rc;
>
> - 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)
> --
> 2.52.0
>
BR, Jarkko
^ permalink raw reply
* Re: [PATCH v2 2/2] KEYS: trusted: Store parent's name to the encoded keys
From: Jarkko Sakkinen @ 2025-12-05 3:11 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: <20251205030205.140842-3-jarkko@kernel.org>
On Fri, Dec 05, 2025 at 05:02:05AM +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.
>
> [1] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt
>
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> ---
> v2:
> - A new patch.
> ---
> security/keys/trusted-keys/tpm2key.asn1 | 17 +++-
> security/keys/trusted-keys/trusted_tpm2.c | 97 ++++++++++++++++-------
> 2 files changed, 84 insertions(+), 30 deletions(-)
>
> diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
> index f57f869ad600..080f0e399982 100644
> --- a/security/keys/trusted-keys/tpm2key.asn1
> +++ b/security/keys/trusted-keys/tpm2key.asn1
> @@ -1,11 +1,26 @@
> ---
> --- ASN.1 for TPM 2.0 keys
> ---
> +TPMPolicy ::= SEQUENCE {
> + commandCode [0] EXPLICIT INTEGER,
> + commandPolicy [1] EXPLICIT OCTET STRING
> +}
> +
> +TPMAuthPolicy ::= SEQUENCE {
> + name [0] EXPLICIT UTF8String OPTIONAL,
> + policy [1] EXPLICIT SEQUENCE OF TPMPolicy
> +}
>
> TPMKey ::= SEQUENCE {
> type OBJECT IDENTIFIER ({tpm2_key_type}),
> emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL,
> + policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL,
> + secret [2] EXPLICIT OCTET STRING OPTIONAL,
> + authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL,
> + description [4] EXPLICIT UTF8String OPTIONAL,
> + rsaParent [5] EXPLICIT BOOLEAN OPTIONAL,
> + parentName [6] EXPLICIT OCTET STRING ({tpm2_key_parent_name}),
> parent INTEGER ({tpm2_key_parent}),
> pubkey OCTET STRING ({tpm2_key_pub}),
> privkey OCTET STRING ({tpm2_key_priv})
> - }
> +}
> diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> index 88bafbcc011a..3608dc9f7fa4 100644
> --- a/security/keys/trusted-keys/trusted_tpm2.c
> +++ b/security/keys/trusted-keys/trusted_tpm2.c
> @@ -20,16 +20,26 @@
>
> static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 };
>
> +enum tpm_key_tag {
> + TPM_KEY_TAG_EMPTY_AUTH = 0,
> + TPM_KEY_TAG_POLICY = 1,
> + TPM_KEY_TAG_SECRET = 2,
> + TPM_KEY_TAG_AUTH_POLICY = 3,
> + TPM_KEY_TAG_DESCRIPTION = 4,
> + TPM_KEY_TAG_RSA_PARENT = 5,
> + TPM_KEY_TAG_PARENT_NAME = 6,
> +};
> +
> static int tpm2_key_encode(struct trusted_key_payload *payload,
> struct trusted_key_options *options,
> - u8 *src, u32 len)
> + u8 *src, u32 len, u8 *parent_name,
> + u16 parent_name_size)
> {
> const int SCRATCH_SIZE = PAGE_SIZE;
> - u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
> - u8 *work = scratch, *work1;
> - u8 *end_work = scratch + SCRATCH_SIZE;
> - u8 *priv, *pub;
> + u8 *end_work, *name_end;
> u16 priv_len, pub_len;
> + u8 *work, *work1;
> + u8 *priv, *pub;
> int ret;
>
> priv_len = get_unaligned_be16(src) + 2;
> @@ -40,23 +50,41 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
> pub_len = get_unaligned_be16(src) + 2;
> pub = src;
>
> + u8 *scratch __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
> if (!scratch)
> return -ENOMEM;
>
> + work = scratch;
> + end_work = scratch + SCRATCH_SIZE;
> +
> work = asn1_encode_oid(work, end_work, tpm2key_oid,
> asn1_oid_len(tpm2key_oid));
>
> if (options->blobauth_len == 0) {
> - unsigned char bool[3], *w = bool;
> - /* tag 0 is emptyAuth */
> - w = asn1_encode_boolean(w, w + sizeof(bool), true);
> - if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) {
> - ret = PTR_ERR(w);
> - goto err;
> + u8 *bool_end;
> + u8 bool[3];
> +
> + bool_end = asn1_encode_boolean(&bool[0], &bool[sizeof(bool)],
> + true);
> + if (IS_ERR(bool_end)) {
> + pr_err("BUG: Boolean failed to encode\n");
> + return PTR_ERR(bool_end);
> }
> - work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
> +
> + work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_EMPTY_AUTH,
> + bool, bool_end - bool);
> }
>
> + u8 *name_encoded __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
> + if (!name_encoded)
> + return -ENOMEM;
> +
> + name_end = asn1_encode_octet_string(&name_encoded[0],
> + &name_encoded[SCRATCH_SIZE],
> + parent_name, parent_name_size);
> + work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_PARENT_NAME,
> + name_encoded, name_end - name_encoded);
> +
> /*
> * Assume both octet strings will encode to a 2 byte definite length
> *
> @@ -65,8 +93,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
> */
> if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
> "BUG: scratch buffer is too small")) {
> - ret = -EINVAL;
> - goto err;
> + return -EINVAL;
> }
>
> work = asn1_encode_integer(work, end_work, options->keyhandle);
> @@ -79,15 +106,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
> if (IS_ERR(work1)) {
> ret = PTR_ERR(work1);
> pr_err("BUG: ASN.1 encoder failed with %d\n", ret);
> - goto err;
> + return ret;
> }
>
> - kfree(scratch);
> return work1 - payload->blob;
> -
> -err:
> - kfree(scratch);
> - return ret;
> }
>
> struct tpm2_key_context {
> @@ -96,11 +118,13 @@ struct tpm2_key_context {
> u32 pub_len;
> const u8 *priv;
> u32 priv_len;
> + const u8 *name;
> + u32 name_len;
> };
>
> static int tpm2_key_decode(struct trusted_key_payload *payload,
> struct trusted_key_options *options,
> - u8 **buf)
> + u8 **buf, u8 *parent_name, u16 *parent_name_size)
> {
> int ret;
> struct tpm2_key_context ctx;
> @@ -127,6 +151,8 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
> blob += ctx.priv_len;
>
> memcpy(blob, ctx.pub, ctx.pub_len);
> + memcpy(parent_name, ctx.name, ctx.name_len);
> + *parent_name_size = ctx.name_len;
>
> return 0;
> }
> @@ -190,6 +216,16 @@ int tpm2_key_priv(void *context, size_t hdrlen,
> return 0;
> }
>
> +int tpm2_key_parent_name(void *context, size_t hdrlen, unsigned char tag,
> + const void *value, size_t vlen)
> +{
> + struct tpm2_key_context *ctx = context;
> +
> + ctx->name = value;
> + ctx->name_len = vlen;
> +
> + return 0;
> +}
> /**
> * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
> *
> @@ -347,7 +383,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> goto out;
> }
>
> - blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len);
> + blob_len = tpm2_key_encode(payload, options, &buf.data[offset],
> + blob_len, parent_name, parent_name_size);
> if (blob_len < 0)
> rc = blob_len;
>
> @@ -602,7 +639,7 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
> * 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);
> + rc = tpm2_key_decode(payload, options, &blob, &parent_name[0], &parent_name_size);
> if (rc) {
> blob = payload->blob;
> payload->old_format = 1;
> @@ -613,16 +650,18 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
> if (!options->keyhandle)
> return -EINVAL;
>
> + if (!parent_name_size) {
> + rc = tpm2_read_public(chip, options->keyhandle, parent_name);
> + if (rc < 0)
> + goto out;
> +
> + parent_name_size = rc;
> + }
> +
> rc = tpm_try_get_ops(chip);
> if (rc)
> return rc;
>
> - rc = tpm2_read_public(chip, options->keyhandle, parent_name);
> - if (rc < 0)
> - goto out;
> -
> - parent_name_size = rc;
> -
Relocating this was entirely unintentional. I'll relocate it back to its
original position in the next revision of this patch set.
> rc = tpm2_load_cmd(chip, payload, options, parent_name,
> parent_name_size, blob, &blob_handle);
> if (rc)
> --
> 2.52.0
>
BR, Jarkko
^ permalink raw reply
* [PATCH v2 2/2] KEYS: trusted: Store parent's name to the encoded keys
From: Jarkko Sakkinen @ 2025-12-05 3:02 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: <20251205030205.140842-1-jarkko@kernel.org>
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.
[1] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v2:
- A new patch.
---
security/keys/trusted-keys/tpm2key.asn1 | 17 +++-
security/keys/trusted-keys/trusted_tpm2.c | 97 ++++++++++++++++-------
2 files changed, 84 insertions(+), 30 deletions(-)
diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
index f57f869ad600..080f0e399982 100644
--- a/security/keys/trusted-keys/tpm2key.asn1
+++ b/security/keys/trusted-keys/tpm2key.asn1
@@ -1,11 +1,26 @@
---
--- ASN.1 for TPM 2.0 keys
---
+TPMPolicy ::= SEQUENCE {
+ commandCode [0] EXPLICIT INTEGER,
+ commandPolicy [1] EXPLICIT OCTET STRING
+}
+
+TPMAuthPolicy ::= SEQUENCE {
+ name [0] EXPLICIT UTF8String OPTIONAL,
+ policy [1] EXPLICIT SEQUENCE OF TPMPolicy
+}
TPMKey ::= SEQUENCE {
type OBJECT IDENTIFIER ({tpm2_key_type}),
emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL,
+ policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL,
+ secret [2] EXPLICIT OCTET STRING OPTIONAL,
+ authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL,
+ description [4] EXPLICIT UTF8String OPTIONAL,
+ rsaParent [5] EXPLICIT BOOLEAN OPTIONAL,
+ parentName [6] EXPLICIT OCTET STRING ({tpm2_key_parent_name}),
parent INTEGER ({tpm2_key_parent}),
pubkey OCTET STRING ({tpm2_key_pub}),
privkey OCTET STRING ({tpm2_key_priv})
- }
+}
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 88bafbcc011a..3608dc9f7fa4 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -20,16 +20,26 @@
static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 };
+enum tpm_key_tag {
+ TPM_KEY_TAG_EMPTY_AUTH = 0,
+ TPM_KEY_TAG_POLICY = 1,
+ TPM_KEY_TAG_SECRET = 2,
+ TPM_KEY_TAG_AUTH_POLICY = 3,
+ TPM_KEY_TAG_DESCRIPTION = 4,
+ TPM_KEY_TAG_RSA_PARENT = 5,
+ TPM_KEY_TAG_PARENT_NAME = 6,
+};
+
static int tpm2_key_encode(struct trusted_key_payload *payload,
struct trusted_key_options *options,
- u8 *src, u32 len)
+ u8 *src, u32 len, u8 *parent_name,
+ u16 parent_name_size)
{
const int SCRATCH_SIZE = PAGE_SIZE;
- u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
- u8 *work = scratch, *work1;
- u8 *end_work = scratch + SCRATCH_SIZE;
- u8 *priv, *pub;
+ u8 *end_work, *name_end;
u16 priv_len, pub_len;
+ u8 *work, *work1;
+ u8 *priv, *pub;
int ret;
priv_len = get_unaligned_be16(src) + 2;
@@ -40,23 +50,41 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
pub_len = get_unaligned_be16(src) + 2;
pub = src;
+ u8 *scratch __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
if (!scratch)
return -ENOMEM;
+ work = scratch;
+ end_work = scratch + SCRATCH_SIZE;
+
work = asn1_encode_oid(work, end_work, tpm2key_oid,
asn1_oid_len(tpm2key_oid));
if (options->blobauth_len == 0) {
- unsigned char bool[3], *w = bool;
- /* tag 0 is emptyAuth */
- w = asn1_encode_boolean(w, w + sizeof(bool), true);
- if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) {
- ret = PTR_ERR(w);
- goto err;
+ u8 *bool_end;
+ u8 bool[3];
+
+ bool_end = asn1_encode_boolean(&bool[0], &bool[sizeof(bool)],
+ true);
+ if (IS_ERR(bool_end)) {
+ pr_err("BUG: Boolean failed to encode\n");
+ return PTR_ERR(bool_end);
}
- work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
+
+ work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_EMPTY_AUTH,
+ bool, bool_end - bool);
}
+ u8 *name_encoded __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
+ if (!name_encoded)
+ return -ENOMEM;
+
+ name_end = asn1_encode_octet_string(&name_encoded[0],
+ &name_encoded[SCRATCH_SIZE],
+ parent_name, parent_name_size);
+ work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_PARENT_NAME,
+ name_encoded, name_end - name_encoded);
+
/*
* Assume both octet strings will encode to a 2 byte definite length
*
@@ -65,8 +93,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
*/
if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
"BUG: scratch buffer is too small")) {
- ret = -EINVAL;
- goto err;
+ return -EINVAL;
}
work = asn1_encode_integer(work, end_work, options->keyhandle);
@@ -79,15 +106,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
if (IS_ERR(work1)) {
ret = PTR_ERR(work1);
pr_err("BUG: ASN.1 encoder failed with %d\n", ret);
- goto err;
+ return ret;
}
- kfree(scratch);
return work1 - payload->blob;
-
-err:
- kfree(scratch);
- return ret;
}
struct tpm2_key_context {
@@ -96,11 +118,13 @@ struct tpm2_key_context {
u32 pub_len;
const u8 *priv;
u32 priv_len;
+ const u8 *name;
+ u32 name_len;
};
static int tpm2_key_decode(struct trusted_key_payload *payload,
struct trusted_key_options *options,
- u8 **buf)
+ u8 **buf, u8 *parent_name, u16 *parent_name_size)
{
int ret;
struct tpm2_key_context ctx;
@@ -127,6 +151,8 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
blob += ctx.priv_len;
memcpy(blob, ctx.pub, ctx.pub_len);
+ memcpy(parent_name, ctx.name, ctx.name_len);
+ *parent_name_size = ctx.name_len;
return 0;
}
@@ -190,6 +216,16 @@ int tpm2_key_priv(void *context, size_t hdrlen,
return 0;
}
+int tpm2_key_parent_name(void *context, size_t hdrlen, unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct tpm2_key_context *ctx = context;
+
+ ctx->name = value;
+ ctx->name_len = vlen;
+
+ return 0;
+}
/**
* tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
*
@@ -347,7 +383,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out;
}
- blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len);
+ blob_len = tpm2_key_encode(payload, options, &buf.data[offset],
+ blob_len, parent_name, parent_name_size);
if (blob_len < 0)
rc = blob_len;
@@ -602,7 +639,7 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
* 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);
+ rc = tpm2_key_decode(payload, options, &blob, &parent_name[0], &parent_name_size);
if (rc) {
blob = payload->blob;
payload->old_format = 1;
@@ -613,16 +650,18 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
if (!options->keyhandle)
return -EINVAL;
+ if (!parent_name_size) {
+ rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+ if (rc < 0)
+ goto out;
+
+ parent_name_size = rc;
+ }
+
rc = tpm_try_get_ops(chip);
if (rc)
return rc;
- 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)
--
2.52.0
^ permalink raw reply related
* [PATCH v2 1/2] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-05 3:02 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: <20251205030205.140842-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>
---
v2:
- No changes.
---
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 3a77be7ebf4a..1f561ad3bdcf 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -202,7 +202,8 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
}
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) {
tpm_buf_destroy(&buf);
return rc;
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 4149379665c4..e33be09446ff 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][2 + SHA512_DIGEST_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;
@@ -219,14 +229,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
@@ -243,15 +255,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
@@ -276,36 +287,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);
@@ -613,22 +603,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;
@@ -689,23 +665,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 202da079d500..319ba75dd79a 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -530,7 +530,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);
@@ -544,6 +544,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>
@@ -567,6 +568,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 a7ea4a1c3bed..88bafbcc011a 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -233,8 +233,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
+ u8 parent_name[2 + SHA512_DIGEST_SIZE];
off_t offset = TPM_HEADER_SIZE;
struct tpm_buf buf, sized;
+ u16 parent_name_size;
int blob_len = 0;
int hash;
u32 flags;
@@ -251,6 +253,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;
@@ -268,7 +276,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out_put;
}
- 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;
@@ -355,21 +364,25 @@ 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;
@@ -377,27 +390,13 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
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);
@@ -433,7 +432,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
return rc;
}
- 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;
@@ -465,20 +465,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;
@@ -498,7 +501,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
return rc;
}
- 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;
@@ -573,30 +577,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[2 + SHA512_DIGEST_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: [GIT PULL] capabilities update for v6.19
From: Linus Torvalds @ 2025-12-05 1:20 UTC (permalink / raw)
To: Serge E. Hallyn
Cc: linux-security-module, Linux Kernel Mailing List, Ryan Foster,
Christian Brauner
In-Reply-To: <aTIm6grHZUhRncFH@mail.hallyn.com>
On Thu, Dec 4, 2025 at 4:27 PM Serge E. Hallyn <serge@hallyn.com> wrote:
>
> This contains the capabilities changes for 6.19.
This email was in my spam folder.
The reason appears to be due to it being marked as "DMARC: FAIL".
I think it's simply because your mail domain doesn't have a dmarc
policy at all, and gmail has been getting more strict about having
both dkim and dmarc set up.
Anyway, I obviously found the email, but I thought I'd mention it,
because while I check my spam folder fairly religiously, it's still
mostly a pretty quick scan for possible emails. There's no guarantee I
always notice...
Linus
^ permalink raw reply
* Re: [PATCH] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-05 0:49 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: <aTIXOr3rpI9xufTl@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 1028 bytes --]
On Fri, Dec 05, 2025 at 01:20:30AM +0200, Jarkko Sakkinen wrote:
> On Fri, Dec 05, 2025 at 12:31:27AM +0200, Jarkko Sakkinen wrote:
> > 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>
>
> If ASN.1 blob would contain also name of the parent then zero
> tpm2_read_public() calls would be required i.e., the main bottleneck
> here inherits from the limitations of the file format itself.
Along the lines of attached patch.
BR, Jarkko
[-- Attachment #2: 0001-KEYS-trusted-Extend-TPMKey-ASN.1-definition-with-par.patch --]
[-- Type: text/plain, Size: 7032 bytes --]
From c5fc7e38aae838dd1190b33545a8b1a1696e9ce8 Mon Sep 17 00:00:00 2001
From: Jarkko Sakkinen <jarkko@kernel.org>
Date: Fri, 5 Dec 2025 01:53:33 +0200
Subject: [PATCH] KEYS: trusted: Extend TPMKey ASN.1 definition with
'parentName'
Extend TPMKey ASN.1 definition [1] with an optional 'parentName' attribute
containing TPMT_HA blob for the parent. Encode this attribute for the
generated TPM keys, which allows skipping TPM2_ReadPublic when unsealing
the key.
[1] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
security/keys/trusted-keys/tpm2key.asn1 | 17 ++++-
security/keys/trusted-keys/trusted_tpm2.c | 80 +++++++++++++++--------
2 files changed, 69 insertions(+), 28 deletions(-)
diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
index f57f869ad600..080f0e399982 100644
--- a/security/keys/trusted-keys/tpm2key.asn1
+++ b/security/keys/trusted-keys/tpm2key.asn1
@@ -1,11 +1,26 @@
---
--- ASN.1 for TPM 2.0 keys
---
+TPMPolicy ::= SEQUENCE {
+ commandCode [0] EXPLICIT INTEGER,
+ commandPolicy [1] EXPLICIT OCTET STRING
+}
+
+TPMAuthPolicy ::= SEQUENCE {
+ name [0] EXPLICIT UTF8String OPTIONAL,
+ policy [1] EXPLICIT SEQUENCE OF TPMPolicy
+}
TPMKey ::= SEQUENCE {
type OBJECT IDENTIFIER ({tpm2_key_type}),
emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL,
+ policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL,
+ secret [2] EXPLICIT OCTET STRING OPTIONAL,
+ authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL,
+ description [4] EXPLICIT UTF8String OPTIONAL,
+ rsaParent [5] EXPLICIT BOOLEAN OPTIONAL,
+ parentName [6] EXPLICIT OCTET STRING ({tpm2_key_parent_name}),
parent INTEGER ({tpm2_key_parent}),
pubkey OCTET STRING ({tpm2_key_pub}),
privkey OCTET STRING ({tpm2_key_priv})
- }
+}
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 88bafbcc011a..85fd34457431 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -20,16 +20,26 @@
static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 };
+enum tpm_key_tag {
+ TPM_KEY_TAG_EMPTY_AUTH = 0,
+ TPM_KEY_TAG_POLICY = 1,
+ TPM_KEY_TAG_SECRET = 2,
+ TPM_KEY_TAG_AUTH_POLICY = 3,
+ TPM_KEY_TAG_DESCRIPTION = 4,
+ TPM_KEY_TAG_RSA_PARENT = 5,
+ TPM_KEY_TAG_PARENT_NAME = 6,
+};
+
static int tpm2_key_encode(struct trusted_key_payload *payload,
struct trusted_key_options *options,
- u8 *src, u32 len)
+ u8 *src, u32 len, u8 *parent_name,
+ u16 parent_name_size)
{
const int SCRATCH_SIZE = PAGE_SIZE;
- u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
- u8 *work = scratch, *work1;
- u8 *end_work = scratch + SCRATCH_SIZE;
- u8 *priv, *pub;
+ u8 *end_work, *end_name;
u16 priv_len, pub_len;
+ u8 *work, *work1;
+ u8 *priv, *pub;
int ret;
priv_len = get_unaligned_be16(src) + 2;
@@ -40,9 +50,13 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
pub_len = get_unaligned_be16(src) + 2;
pub = src;
+ u8 *scratch __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
if (!scratch)
return -ENOMEM;
+ work = scratch;
+ end_work = scratch + SCRATCH_SIZE;
+
work = asn1_encode_oid(work, end_work, tpm2key_oid,
asn1_oid_len(tpm2key_oid));
@@ -50,13 +64,22 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
unsigned char bool[3], *w = bool;
/* tag 0 is emptyAuth */
w = asn1_encode_boolean(w, w + sizeof(bool), true);
- if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) {
- ret = PTR_ERR(w);
- goto err;
- }
- work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
+ if (WARN(IS_ERR(w), "BUG: Boolean failed to encode"))
+ return PTR_ERR(w);
+ work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_EMPTY_AUTH,
+ bool, w - bool);
}
+ u8 *name_encoded __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
+ if (!name_encoded)
+ return -ENOMEM;
+
+ end_name = asn1_encode_octet_string(name_encoded,
+ name_encoded + SCRATCH_SIZE,
+ parent_name, parent_name_size);
+ work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_PARENT_NAME,
+ name_encoded, end_name - name_encoded);
+
/*
* Assume both octet strings will encode to a 2 byte definite length
*
@@ -65,8 +88,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
*/
if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
"BUG: scratch buffer is too small")) {
- ret = -EINVAL;
- goto err;
+ return -EINVAL;
}
work = asn1_encode_integer(work, end_work, options->keyhandle);
@@ -79,15 +101,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
if (IS_ERR(work1)) {
ret = PTR_ERR(work1);
pr_err("BUG: ASN.1 encoder failed with %d\n", ret);
- goto err;
+ return ret;
}
- kfree(scratch);
return work1 - payload->blob;
-
-err:
- kfree(scratch);
- return ret;
}
struct tpm2_key_context {
@@ -96,11 +113,13 @@ struct tpm2_key_context {
u32 pub_len;
const u8 *priv;
u32 priv_len;
+ const u8 *name;
+ u32 name_len;
};
static int tpm2_key_decode(struct trusted_key_payload *payload,
struct trusted_key_options *options,
- u8 **buf)
+ u8 **buf, u8 *parent_name, u16 *parent_name_size)
{
int ret;
struct tpm2_key_context ctx;
@@ -127,6 +146,8 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
blob += ctx.priv_len;
memcpy(blob, ctx.pub, ctx.pub_len);
+ memcpy(parent_name, ctx.name, ctx.name_len);
+ *parent_name_size = ctx.name_len;
return 0;
}
@@ -190,6 +211,16 @@ int tpm2_key_priv(void *context, size_t hdrlen,
return 0;
}
+int tpm2_key_parent_name(void *context, size_t hdrlen, unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct tpm2_key_context *ctx = context;
+
+ ctx->name = value;
+ ctx->name_len = vlen;
+
+ return 0;
+}
/**
* tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
*
@@ -347,7 +378,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out;
}
- blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len);
+ blob_len = tpm2_key_encode(payload, options, &buf.data[offset],
+ blob_len, parent_name, parent_name_size);
if (blob_len < 0)
rc = blob_len;
@@ -602,7 +634,7 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
* 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);
+ rc = tpm2_key_decode(payload, options, &blob, &parent_name[0], &parent_name_size);
if (rc) {
blob = payload->blob;
payload->old_format = 1;
@@ -617,12 +649,6 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
if (rc)
return rc;
- 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)
--
2.39.5
^ permalink raw reply related
* [GIT PULL] capabilities update for v6.19
From: Serge E. Hallyn @ 2025-12-05 0:27 UTC (permalink / raw)
To: torvalds, linux-security-module, Linux Kernel Mailing List
Cc: Ryan Foster, Christian Brauner
Hi Linus,
This contains the capabilities changes for 6.19. There is only a single commit,
Clarify the rootid_owns_currentns
which introduces no functional change. Ryan Foster had sent a patch
to add testing of the security/commoncap.c:rootid_owns_currentns()
function. The patch pointed out that this function was not as clear
as it should be.
This commit has two purposes:
1. Clarify the intent of the function in the name
2. Split the function so that the base functionality is easier
to test from a kunit test.
This commit has been in linux-next since November 18 with no reported
issues. Ryan has posted an updated test patch based on this commit.
The following changes since commit 211ddde0823f1442e4ad052a2f30f050145ccada:
Linux 6.18-rc2 (2025-10-19 15:19:16 -1000)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/sergeh/linux.git tags/caps-pr-20251204
for you to fetch changes up to 9891d2f79a9fe9b77ad290f950eb8fa3e375330e:
Clarify the rootid_owns_currentns (2025-11-18 18:00:19 -0600)
----------------------------------------------------------------
Capabilities patch for v6.19
There is only a single commit,
Clarify the rootid_owns_currentns
which introduces no functional change. Ryan Foster had sent a patch
to add testing of the security/commoncap.c:rootid_owns_currentns()
function. The patch pointed out that this function was not as clear
as it should be.
This commit has two purposes:
1. Clarify the intent of the function in the name
2. Split the function so that the base functionality is easier
to test from a kunit test.
This commit has been in linux-next since November 18 with no reported
issues. Ryan has posted an updated test patch based on this commit.
----------------------------------------------------------------
Serge Hallyn (1):
Clarify the rootid_owns_currentns
security/commoncap.c | 34 ++++++++++++++++++++++------------
1 file changed, 22 insertions(+), 12 deletions(-)
^ permalink raw reply
* Re: [PATCH] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-04 23:20 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: <20251204223128.435109-1-jarkko@kernel.org>
On Fri, Dec 05, 2025 at 12:31:27AM +0200, Jarkko Sakkinen wrote:
> 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>
If ASN.1 blob would contain also name of the parent then zero
tpm2_read_public() calls would be required i.e., the main bottleneck
here inherits from the limitations of the file format itself.
BR, Jarkko
^ permalink raw reply
* [PATCH] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-04 22:31 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
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 3a77be7ebf4a..1f561ad3bdcf 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -202,7 +202,8 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
}
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) {
tpm_buf_destroy(&buf);
return rc;
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 4149379665c4..e33be09446ff 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][2 + SHA512_DIGEST_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;
@@ -219,14 +229,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
@@ -243,15 +255,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
@@ -276,36 +287,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);
@@ -613,22 +603,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;
@@ -689,23 +665,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 202da079d500..319ba75dd79a 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -530,7 +530,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);
@@ -544,6 +544,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>
@@ -567,6 +568,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 a7ea4a1c3bed..88bafbcc011a 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -233,8 +233,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
+ u8 parent_name[2 + SHA512_DIGEST_SIZE];
off_t offset = TPM_HEADER_SIZE;
struct tpm_buf buf, sized;
+ u16 parent_name_size;
int blob_len = 0;
int hash;
u32 flags;
@@ -251,6 +253,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;
@@ -268,7 +276,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out_put;
}
- 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;
@@ -355,21 +364,25 @@ 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;
@@ -377,27 +390,13 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
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);
@@ -433,7 +432,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
return rc;
}
- 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;
@@ -465,20 +465,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;
@@ -498,7 +501,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
return rc;
}
- 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;
@@ -573,30 +577,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[2 + SHA512_DIGEST_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] security: Add KUnit tests for kuid_root_in_ns and vfsuid_root_in_currentns
From: Ryan Foster @ 2025-12-04 21:56 UTC (permalink / raw)
To: linux-security-module; +Cc: serge, paul, linux-kernel, Ryan Foster
In-Reply-To: <aSXICxT6u0Rx1FhW@mail.hallyn.com>
Add comprehensive KUnit tests for the namespace-related capability
functions that Serge Hallyn refactored in commit 9891d2f79a9f
("Clarify the rootid_owns_currentns").
The tests verify:
- Basic functionality: UID 0 in init namespace, invalid vfsuid, non-zero UIDs
- Actual namespace traversal: Creating user namespaces with different UID
mappings where uid 0 maps to different kuids (e.g., 1000, 2000, 3000)
- Hierarchy traversal: Testing multiple nested namespaces to verify
correct namespace hierarchy traversal
This addresses the feedback to "test the actual functionality" by creating
real user namespaces with different values for the namespace's uid 0, rather
than just basic input validation.
The test file is included at the end of commoncap.c when
CONFIG_SECURITY_COMMONCAP_KUNIT_TEST is enabled, following the standard
kernel pattern (e.g., scsi_lib.c, ext4/mballoc.c). This allows tests to
access static functions in the same compilation unit without modifying
production code based on test configuration.
All 7 tests pass:
- test_vfsuid_root_in_currentns_init_ns
- test_vfsuid_root_in_currentns_invalid
- test_vfsuid_root_in_currentns_nonzero
- test_kuid_root_in_ns_init_ns_uid0
- test_kuid_root_in_ns_init_ns_nonzero
- test_kuid_root_in_ns_with_mapping
- test_kuid_root_in_ns_with_different_mappings
---
security/Kconfig | 17 +++
security/commoncap.c | 4 +
security/commoncap_test.c | 290 ++++++++++++++++++++++++++++++++++++++
3 files changed, 311 insertions(+)
create mode 100644 security/commoncap_test.c
diff --git a/security/Kconfig b/security/Kconfig
index 285f284dfcac..c7b3f42ef875 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -284,6 +284,23 @@ config LSM
If unsure, leave this as the default.
+config SECURITY_COMMONCAP_KUNIT_TEST
+ bool "Build KUnit tests for commoncap" if !KUNIT_ALL_TESTS
+ depends on KUNIT=y
+ default KUNIT_ALL_TESTS
+ help
+ This builds the commoncap KUnit tests.
+
+ KUnit tests run during boot and output the results to the debug log
+ in TAP format (https://testanything.org/). Only useful for kernel devs
+ running KUnit test harness and are not for inclusion into a
+ production build.
+
+ For more information on KUnit and unit tests in general please refer
+ to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+ If unsure, say N.
+
source "security/Kconfig.hardening"
endmenu
diff --git a/security/commoncap.c b/security/commoncap.c
index 8a23dfab7fac..3399535808fe 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -1521,3 +1521,7 @@ DEFINE_LSM(capability) = {
};
#endif /* CONFIG_SECURITY */
+
+#ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
+#include "commoncap_test.c"
+#endif
diff --git a/security/commoncap_test.c b/security/commoncap_test.c
new file mode 100644
index 000000000000..1088364a54e6
--- /dev/null
+++ b/security/commoncap_test.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * KUnit tests for commoncap.c security functions
+ *
+ * Tests for security-critical functions in the capability subsystem,
+ * particularly namespace-related capability checks.
+ */
+
+#include <kunit/test.h>
+#include <linux/user_namespace.h>
+#include <linux/uidgid.h>
+#include <linux/cred.h>
+#include <linux/mnt_idmapping.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/refcount.h>
+
+#ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
+
+/* Functions are static in commoncap.c, but we can call them since we're
+ * included in the same compilation unit when tests are enabled.
+ */
+
+/**
+ * test_vfsuid_root_in_currentns_init_ns - Test vfsuid_root_in_currentns with init ns
+ *
+ * Verifies that UID 0 in the init namespace correctly owns the current
+ * namespace when running in init_user_ns.
+ *
+ * @test: KUnit test context
+ */
+static void test_vfsuid_root_in_currentns_init_ns(struct kunit *test)
+{
+ vfsuid_t vfsuid;
+ kuid_t kuid;
+
+ /* Create UID 0 in init namespace */
+ kuid = KUIDT_INIT(0);
+ vfsuid = VFSUIDT_INIT(kuid);
+
+ /* In init namespace, UID 0 should own current namespace */
+ KUNIT_EXPECT_TRUE(test, vfsuid_root_in_currentns(vfsuid));
+}
+
+/**
+ * test_vfsuid_root_in_currentns_invalid - Test vfsuid_root_in_currentns with invalid vfsuid
+ *
+ * Verifies that an invalid vfsuid correctly returns false.
+ *
+ * @test: KUnit test context
+ */
+static void test_vfsuid_root_in_currentns_invalid(struct kunit *test)
+{
+ vfsuid_t invalid_vfsuid;
+
+ /* Use the predefined invalid vfsuid */
+ invalid_vfsuid = INVALID_VFSUID;
+
+ /* Invalid vfsuid should return false */
+ KUNIT_EXPECT_FALSE(test, vfsuid_root_in_currentns(invalid_vfsuid));
+}
+
+/**
+ * test_vfsuid_root_in_currentns_nonzero - Test vfsuid_root_in_currentns with non-zero UID
+ *
+ * Verifies that a non-zero UID correctly returns false.
+ *
+ * @test: KUnit test context
+ */
+static void test_vfsuid_root_in_currentns_nonzero(struct kunit *test)
+{
+ vfsuid_t vfsuid;
+ kuid_t kuid;
+
+ /* Create a non-zero UID */
+ kuid = KUIDT_INIT(1000);
+ vfsuid = VFSUIDT_INIT(kuid);
+
+ /* Non-zero UID should return false */
+ KUNIT_EXPECT_FALSE(test, vfsuid_root_in_currentns(vfsuid));
+}
+
+/**
+ * test_kuid_root_in_ns_init_ns_uid0 - Test kuid_root_in_ns with init namespace and UID 0
+ *
+ * Verifies that kuid_root_in_ns correctly identifies UID 0 in init namespace.
+ * This tests the core namespace traversal logic. In init namespace, UID 0
+ * maps to itself, so it should own the namespace.
+ *
+ * @test: KUnit test context
+ */
+static void test_kuid_root_in_ns_init_ns_uid0(struct kunit *test)
+{
+ kuid_t kuid;
+ struct user_namespace *init_ns;
+
+ kuid = KUIDT_INIT(0);
+ init_ns = &init_user_ns;
+
+ /* UID 0 should own init namespace */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(kuid, init_ns));
+}
+
+/**
+ * test_kuid_root_in_ns_init_ns_nonzero - Test kuid_root_in_ns with init namespace and non-zero UID
+ *
+ * Verifies that kuid_root_in_ns correctly rejects non-zero UIDs in init namespace.
+ * Only UID 0 should own a namespace.
+ *
+ * @test: KUnit test context
+ */
+static void test_kuid_root_in_ns_init_ns_nonzero(struct kunit *test)
+{
+ kuid_t kuid;
+ struct user_namespace *init_ns;
+
+ kuid = KUIDT_INIT(1000);
+ init_ns = &init_user_ns;
+
+ /* Non-zero UID should not own namespace */
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(kuid, init_ns));
+}
+
+/**
+ * create_test_user_ns_with_mapping - Create a mock user namespace with UID mapping
+ *
+ * Creates a minimal user namespace structure for testing where uid 0 in the
+ * namespace maps to a specific kuid in the parent namespace.
+ *
+ * @test: KUnit test context
+ * @parent_ns: Parent namespace (typically init_user_ns)
+ * @mapped_kuid: The kuid that uid 0 in this namespace maps to in parent
+ *
+ * Returns: Pointer to allocated namespace, or NULL on failure
+ */
+static struct user_namespace *create_test_user_ns_with_mapping(struct kunit *test,
+ struct user_namespace *parent_ns,
+ kuid_t mapped_kuid)
+{
+ struct user_namespace *ns;
+ struct uid_gid_extent extent;
+
+ /* Allocate a test namespace - use kzalloc to zero all fields */
+ ns = kunit_kzalloc(test, sizeof(*ns), GFP_KERNEL);
+ if (!ns)
+ return NULL;
+
+ /* Initialize basic namespace structure fields */
+ ns->parent = parent_ns;
+ ns->level = parent_ns ? parent_ns->level + 1 : 0;
+ ns->owner = mapped_kuid;
+ ns->group = KGIDT_INIT(0);
+
+ /* Initialize ns_common structure */
+ refcount_set(&ns->ns.__ns_ref, 1);
+ ns->ns.inum = 0; /* Mock inum */
+
+ /* Set up uid mapping: uid 0 in this namespace maps to mapped_kuid in parent
+ * Format: first (uid in ns) : lower_first (kuid in parent) : count
+ * So: uid 0 in ns -> kuid mapped_kuid in parent
+ * This means from_kuid(ns, mapped_kuid) returns 0
+ */
+ extent.first = 0; /* uid 0 in this namespace */
+ extent.lower_first = __kuid_val(mapped_kuid); /* maps to this kuid in parent */
+ extent.count = 1;
+
+ ns->uid_map.extent[0] = extent;
+ ns->uid_map.nr_extents = 1;
+
+ /* Set up gid mapping: gid 0 maps to gid 0 in parent (simplified) */
+ extent.first = 0;
+ extent.lower_first = 0;
+ extent.count = 1;
+
+ ns->gid_map.extent[0] = extent;
+ ns->gid_map.nr_extents = 1;
+
+ return ns;
+}
+
+/**
+ * test_kuid_root_in_ns_with_mapping - Test kuid_root_in_ns with namespace where uid 0
+ * maps to different kuid
+ *
+ * Creates a user namespace where uid 0 maps to kuid 1000 in the parent namespace.
+ * Verifies that kuid_root_in_ns correctly identifies kuid 1000 as owning the namespace.
+ *
+ * Note: kuid_root_in_ns walks up the namespace hierarchy, so it checks the current
+ * namespace first, then parent, then parent's parent, etc. So:
+ * - kuid 1000 owns test_ns because from_kuid(test_ns, 1000) == 0
+ * - kuid 0 also owns test_ns because from_kuid(init_user_ns, 0) == 0
+ * (checked in parent)
+ *
+ * This tests the actual functionality as requested: creating namespaces with
+ * different values for the namespace's uid 0.
+ *
+ * @test: KUnit test context
+ */
+static void test_kuid_root_in_ns_with_mapping(struct kunit *test)
+{
+ struct user_namespace *test_ns;
+ struct user_namespace *parent_ns;
+ kuid_t mapped_kuid, other_kuid;
+
+ parent_ns = &init_user_ns;
+ mapped_kuid = KUIDT_INIT(1000);
+ other_kuid = KUIDT_INIT(2000);
+
+ test_ns = create_test_user_ns_with_mapping(test, parent_ns, mapped_kuid);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_ns);
+
+ /* kuid 1000 should own test_ns because it maps to uid 0 in test_ns */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(mapped_kuid, test_ns));
+
+ /* kuid 0 should also own test_ns (checked via parent init_user_ns) */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), test_ns));
+
+ /* Other kuids should not own test_ns */
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(other_kuid, test_ns));
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(500), test_ns));
+}
+
+/**
+ * test_kuid_root_in_ns_with_different_mappings - Test with multiple namespaces
+ *
+ * Creates multiple user namespaces with different UID mappings to verify
+ * that kuid_root_in_ns correctly handles different namespace hierarchies.
+ *
+ * Since kuid_root_in_ns walks up the hierarchy, kuids that map to 0 in init_user_ns
+ * will own all namespaces, while kuids that only map to 0 in specific namespaces
+ * will only own those namespaces and their children.
+ *
+ * @test: KUnit test context
+ */
+static void test_kuid_root_in_ns_with_different_mappings(struct kunit *test)
+{
+ struct user_namespace *ns1, *ns2, *ns3;
+
+ /* Create ns1 where uid 0 maps to kuid 1000 */
+ ns1 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(1000));
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns1);
+
+ /* Create ns2 where uid 0 maps to kuid 2000 */
+ ns2 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(2000));
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns2);
+
+ /* Create ns3 as a child of ns1 where uid 0 maps to kuid 3000 */
+ ns3 = create_test_user_ns_with_mapping(test, ns1, KUIDT_INIT(3000));
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns3);
+
+ /* Test ns1: kuid 1000 owns it, kuid 0 owns it (via parent), kuid 2000 does not */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns1));
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns1));
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns1));
+
+ /* Test ns2: kuid 2000 owns it, kuid 0 owns it (via parent), kuid 1000 does not */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns2));
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns2));
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns2));
+
+ /* Test ns3: kuid 3000 owns it, kuid 1000 owns it (via parent ns1),
+ * kuid 0 owns it (via init_user_ns), kuid 2000 does not
+ */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(3000), ns3));
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns3));
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns3));
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns3));
+}
+
+static struct kunit_case commoncap_test_cases[] = {
+ KUNIT_CASE(test_vfsuid_root_in_currentns_init_ns),
+ KUNIT_CASE(test_vfsuid_root_in_currentns_invalid),
+ KUNIT_CASE(test_vfsuid_root_in_currentns_nonzero),
+ KUNIT_CASE(test_kuid_root_in_ns_init_ns_uid0),
+ KUNIT_CASE(test_kuid_root_in_ns_init_ns_nonzero),
+ KUNIT_CASE(test_kuid_root_in_ns_with_mapping),
+ KUNIT_CASE(test_kuid_root_in_ns_with_different_mappings),
+ {}
+};
+
+static struct kunit_suite commoncap_test_suite = {
+ .name = "commoncap",
+ .test_cases = commoncap_test_cases,
+};
+
+kunit_test_suite(commoncap_test_suite);
+
+MODULE_LICENSE("GPL");
+
+#endif /* CONFIG_SECURITY_COMMONCAP_KUNIT_TEST */
--
2.43.0
^ permalink raw reply related
* [PATCH v4 4/4] tpm2-sessions: Open code tpm_buf_append_hmac_session()
From: Jarkko Sakkinen @ 2025-12-04 19:47 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, open list,
Jarkko Sakkinen, Jonathan McDowell, James Bottomley, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <20251204194743.69035-1-jarkko@kernel.org>
From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Open code 'tpm_buf_append_hmac_session_opt' to the call site, as it only
masks a call sequence and does otherwise nothing particularly useful.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Reviewed-by: Jonathan McDowell <noodles@meta.com>
---
drivers/char/tpm/tpm2-cmd.c | 14 +++++++++++---
include/linux/tpm.h | 23 -----------------------
security/keys/trusted-keys/trusted_tpm2.c | 12 ++++++++++--
3 files changed, 21 insertions(+), 28 deletions(-)
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index ce0a1c6b0596..3a77be7ebf4a 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -282,9 +282,17 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
do {
tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
- tpm_buf_append_hmac_session_opt(chip, &buf, TPM2_SA_ENCRYPT
- | TPM2_SA_CONTINUE_SESSION,
- NULL, 0);
+ if (tpm2_chip_auth(chip)) {
+ tpm_buf_append_hmac_session(chip, &buf,
+ TPM2_SA_ENCRYPT |
+ TPM2_SA_CONTINUE_SESSION,
+ 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);
+ }
tpm_buf_append_u16(&buf, num_bytes);
err = tpm_buf_fill_hmac_session(chip, &buf);
if (err) {
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index afa51723296a..202da079d500 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -536,29 +536,6 @@ void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
int passphraselen);
void tpm_buf_append_auth(struct tpm_chip *chip, struct tpm_buf *buf,
u8 *passphrase, int passphraselen);
-static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
- struct tpm_buf *buf,
- u8 attributes,
- u8 *passphrase,
- int passphraselen)
-{
- struct tpm_header *head;
- int offset;
-
- if (tpm2_chip_auth(chip)) {
- tpm_buf_append_hmac_session(chip, buf, attributes, passphrase, passphraselen);
- } else {
- offset = buf->handles * 4 + TPM_HEADER_SIZE;
- head = (struct tpm_header *)buf->data;
-
- /*
- * If the only sessions are optional, the command tag must change to
- * TPM2_ST_NO_SESSIONS.
- */
- if (tpm_buf_length(buf) == offset)
- head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
- }
-}
#ifdef CONFIG_TCG_TPM2_HMAC
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 5b205279584b..a7ea4a1c3bed 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -481,8 +481,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
struct trusted_key_options *options,
u32 blob_handle)
{
+ struct tpm_header *head;
struct tpm_buf buf;
u16 data_len;
+ int offset;
u8 *data;
int rc;
@@ -519,8 +521,14 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
tpm2_buf_append_auth(&buf, options->policyhandle,
NULL /* nonce */, 0, 0,
options->blobauth, options->blobauth_len);
- tpm_buf_append_hmac_session_opt(chip, &buf, TPM2_SA_ENCRYPT,
- NULL, 0);
+ 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
* [PATCH v4 1/4] tpm2-sessions: Fix out of range indexing in name_size
From: Jarkko Sakkinen @ 2025-12-04 19:47 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, open list, stable,
Jonathan McDowell, James Bottomley, Mimi Zohar, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, Ard Biesheuvel,
open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <20251204194743.69035-1-jarkko@kernel.org>
'name_size' does not have any range checks, and it just directly indexes
with TPM_ALG_ID, which could lead into memory corruption at worst.
Address the issue by only processing known values and returning -EINVAL for
unrecognized values.
Make also 'tpm_buf_append_name' and 'tpm_buf_fill_hmac_session' fallible so
that errors are detected before causing any spurious TPM traffic.
End also the authorization session on failure in both of the functions, as
the session state would be then by definition corrupted.
Cc: stable@vger.kernel.org # v6.10+
Fixes: 1085b8276bb4 ("tpm: Add the rest of the session HMAC API")
Reviewed-by: Jonathan McDowell <noodles@meta.com>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v4:
- Removed spurious empty line.
v3:
- Fix pr_warn() format string in name_size().
- Fix !CONFIG_TPM2_HMAC compilation.
v2:
- Wrote a better short summary.
- Addressed remarks in https://lore.kernel.org/linux-integrity/aS8TIeviaippVAha@earth.li/
- Use -EIO consistently in tpm2_fill_hmac_session. These are not input value
errors. They could only spun from malformed (kernel) state.
- name_size did not have a proper default-case. Reorganize the
fallback into that.
---
drivers/char/tpm/tpm2-cmd.c | 23 +++-
drivers/char/tpm/tpm2-sessions.c | 132 +++++++++++++++-------
include/linux/tpm.h | 13 ++-
security/keys/trusted-keys/trusted_tpm2.c | 29 ++++-
4 files changed, 142 insertions(+), 55 deletions(-)
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index dd502322f499..be4a9c7f2e1a 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -199,7 +199,11 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
}
if (!disable_pcr_integrity) {
- tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
+ rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ return rc;
+ }
tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
} else {
tpm_buf_append_handle(chip, &buf, pcr_idx);
@@ -214,8 +218,14 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
chip->allocated_banks[i].digest_size);
}
- if (!disable_pcr_integrity)
- tpm_buf_fill_hmac_session(chip, &buf);
+ if (!disable_pcr_integrity) {
+ rc = tpm_buf_fill_hmac_session(chip, &buf);
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ return rc;
+ }
+ }
+
rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
if (!disable_pcr_integrity)
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
@@ -273,7 +283,12 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
| TPM2_SA_CONTINUE_SESSION,
NULL, 0);
tpm_buf_append_u16(&buf, num_bytes);
- tpm_buf_fill_hmac_session(chip, &buf);
+ err = tpm_buf_fill_hmac_session(chip, &buf);
+ if (err) {
+ tpm_buf_destroy(&buf);
+ return err;
+ }
+
err = tpm_transmit_cmd(chip, &buf,
offsetof(struct tpm2_get_random_out,
buffer),
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 6d03c224e6b2..385014dbca39 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -144,16 +144,23 @@ struct tpm2_auth {
/*
* Name Size based on TPM algorithm (assumes no hash bigger than 255)
*/
-static u8 name_size(const u8 *name)
+static int name_size(const u8 *name)
{
- static u8 size_map[] = {
- [TPM_ALG_SHA1] = SHA1_DIGEST_SIZE,
- [TPM_ALG_SHA256] = SHA256_DIGEST_SIZE,
- [TPM_ALG_SHA384] = SHA384_DIGEST_SIZE,
- [TPM_ALG_SHA512] = SHA512_DIGEST_SIZE,
- };
- u16 alg = get_unaligned_be16(name);
- return size_map[alg] + 2;
+ u16 hash_alg = get_unaligned_be16(name);
+
+ switch (hash_alg) {
+ case TPM_ALG_SHA1:
+ return SHA1_DIGEST_SIZE + 2;
+ case TPM_ALG_SHA256:
+ return SHA256_DIGEST_SIZE + 2;
+ case TPM_ALG_SHA384:
+ return SHA384_DIGEST_SIZE + 2;
+ case TPM_ALG_SHA512:
+ return SHA512_DIGEST_SIZE + 2;
+ default:
+ pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg);
+ return -EINVAL;
+ }
}
static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
@@ -161,6 +168,7 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
struct tpm_header *head = (struct tpm_header *)buf->data;
off_t offset = TPM_HEADER_SIZE;
u32 tot_len = be32_to_cpu(head->length);
+ int ret;
u32 val;
/* we're starting after the header so adjust the length */
@@ -173,8 +181,13 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
offset += val;
/* name */
val = tpm_buf_read_u16(buf, &offset);
- if (val != name_size(&buf->data[offset]))
+ ret = name_size(&buf->data[offset]);
+ if (ret < 0)
+ return ret;
+
+ if (val != ret)
return -EINVAL;
+
memcpy(name, &buf->data[offset], val);
/* forget the rest */
return 0;
@@ -221,46 +234,72 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, char *name)
* As with most tpm_buf operations, success is assumed because failure
* will be caused by an incorrect programming model and indicated by a
* kernel message.
+ *
+ * Ends the authorization session on failure.
*/
-void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name)
+int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
+ u32 handle, u8 *name)
{
#ifdef CONFIG_TCG_TPM2_HMAC
enum tpm2_mso_type mso = tpm2_handle_mso(handle);
struct tpm2_auth *auth;
int slot;
+ int ret;
#endif
if (!tpm2_chip_auth(chip)) {
tpm_buf_append_handle(chip, buf, handle);
- return;
+ return 0;
}
#ifdef CONFIG_TCG_TPM2_HMAC
slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
if (slot >= AUTH_MAX_NAMES) {
- dev_err(&chip->dev, "TPM: too many handles\n");
- return;
+ dev_err(&chip->dev, "too many handles\n");
+ ret = -EIO;
+ goto err;
}
auth = chip->auth;
- WARN(auth->session != tpm_buf_length(buf),
- "name added in wrong place\n");
+ if (auth->session != tpm_buf_length(buf)) {
+ dev_err(&chip->dev, "session state malformed");
+ ret = -EIO;
+ goto err;
+ }
tpm_buf_append_u32(buf, handle);
auth->session += 4;
if (mso == TPM2_MSO_PERSISTENT ||
mso == TPM2_MSO_VOLATILE ||
mso == TPM2_MSO_NVRAM) {
- if (!name)
- tpm2_read_public(chip, handle, auth->name[slot]);
+ if (!name) {
+ ret = tpm2_read_public(chip, handle, auth->name[slot]);
+ if (ret)
+ goto err;
+ }
} else {
- if (name)
- dev_err(&chip->dev, "TPM: Handle does not require name but one is specified\n");
+ 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(name));
+ if (name) {
+ ret = name_size(name);
+ if (ret < 0)
+ goto err;
+
+ memcpy(auth->name[slot], name, ret);
+ }
+#endif
+ return 0;
+
+#ifdef CONFIG_TCG_TPM2_HMAC
+err:
+ tpm2_end_auth_session(chip);
+ return tpm_ret_to_err(ret);
#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -533,11 +572,9 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip,
* encryption key and encrypts the first parameter of the command
* buffer with it.
*
- * As with most tpm_buf operations, success is assumed because failure
- * will be caused by an incorrect programming model and indicated by a
- * kernel message.
+ * Ends the authorization session on failure.
*/
-void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
+int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
{
u32 cc, handles, val;
struct tpm2_auth *auth = chip->auth;
@@ -549,9 +586,12 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
u8 cphash[SHA256_DIGEST_SIZE];
struct sha256_ctx sctx;
struct hmac_sha256_ctx hctx;
+ int ret;
- if (!auth)
- return;
+ if (!auth) {
+ ret = -EIO;
+ goto err;
+ }
/* save the command code in BE format */
auth->ordinal = head->ordinal;
@@ -560,9 +600,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
i = tpm2_find_cc(chip, cc);
if (i < 0) {
- dev_err(&chip->dev, "Command 0x%x not found in TPM\n", cc);
- return;
+ dev_err(&chip->dev, "command 0x%08x not found\n", cc);
+ ret = -EIO;
+ goto err;
}
+
attrs = chip->cc_attrs_tbl[i];
handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
@@ -576,9 +618,9 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
u32 handle = tpm_buf_read_u32(buf, &offset_s);
if (auth->name_h[i] != handle) {
- dev_err(&chip->dev, "TPM: handle %d wrong for name\n",
- i);
- return;
+ dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
+ ret = -EIO;
+ goto err;
}
}
/* point offset_s to the start of the sessions */
@@ -609,12 +651,14 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
offset_s += len;
}
if (offset_s != offset_p) {
- dev_err(&chip->dev, "TPM session length is incorrect\n");
- return;
+ dev_err(&chip->dev, "session length is incorrect\n");
+ ret = -EIO;
+ goto err;
}
if (!hmac) {
- dev_err(&chip->dev, "TPM could not find HMAC session\n");
- return;
+ dev_err(&chip->dev, "could not find HMAC session\n");
+ ret = -EIO;
+ goto err;
}
/* encrypt before HMAC */
@@ -646,8 +690,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
if (mso == TPM2_MSO_PERSISTENT ||
mso == TPM2_MSO_VOLATILE ||
mso == TPM2_MSO_NVRAM) {
- sha256_update(&sctx, auth->name[i],
- name_size(auth->name[i]));
+ 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]);
@@ -668,6 +715,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
hmac_sha256_update(&hctx, &auth->attrs, 1);
hmac_sha256_final(&hctx, hmac);
+ return 0;
+
+err:
+ tpm2_end_auth_session(chip);
+ return ret;
}
EXPORT_SYMBOL(tpm_buf_fill_hmac_session);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 3d8f7d1ce2b8..aa816b144ab3 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -529,8 +529,8 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
#endif
}
-void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name);
+int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
+ u32 handle, u8 *name);
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
u8 attributes, u8 *passphrase,
int passphraselen);
@@ -563,7 +563,7 @@ static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
#ifdef CONFIG_TCG_TPM2_HMAC
int tpm2_start_auth_session(struct tpm_chip *chip);
-void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
+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);
@@ -577,10 +577,13 @@ static inline int tpm2_start_auth_session(struct tpm_chip *chip)
static inline void tpm2_end_auth_session(struct tpm_chip *chip)
{
}
-static inline void tpm_buf_fill_hmac_session(struct tpm_chip *chip,
- struct tpm_buf *buf)
+
+static inline int tpm_buf_fill_hmac_session(struct tpm_chip *chip,
+ struct tpm_buf *buf)
{
+ return 0;
}
+
static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
struct tpm_buf *buf,
int rc)
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 8bc6efa8accb..5b205279584b 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -268,7 +268,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out_put;
}
- tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ if (rc)
+ goto out;
+
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
options->keyauth, TPM_DIGEST_SIZE);
@@ -316,7 +319,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out;
}
- tpm_buf_fill_hmac_session(chip, &buf);
+ rc = tpm_buf_fill_hmac_session(chip, &buf);
+ if (rc)
+ goto out;
+
rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
if (rc)
@@ -427,7 +433,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
return rc;
}
- tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ if (rc)
+ goto out;
+
tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
TPM_DIGEST_SIZE);
@@ -439,7 +448,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
goto out;
}
- tpm_buf_fill_hmac_session(chip, &buf);
+ rc = tpm_buf_fill_hmac_session(chip, &buf);
+ if (rc)
+ goto out;
+
rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
if (!rc)
@@ -484,7 +496,9 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
return rc;
}
- tpm_buf_append_name(chip, &buf, blob_handle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ if (rc)
+ goto out;
if (!options->policyhandle) {
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
@@ -509,7 +523,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
NULL, 0);
}
- tpm_buf_fill_hmac_session(chip, &buf);
+ rc = tpm_buf_fill_hmac_session(chip, &buf);
+ if (rc)
+ goto out;
+
rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
--
2.52.0
^ permalink raw reply related
* Re: [PATCH v3 1/4] tpm2-sessions: fix out of range indexing in name_size
From: Jarkko Sakkinen @ 2025-12-04 18:47 UTC (permalink / raw)
To: Jonathan McDowell
Cc: linux-integrity, Peter Huewe, Jason Gunthorpe, open list, stable,
James Bottomley, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, Ard Biesheuvel,
open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <aTGkno0fzQMHXc7X@earth.li>
On Thu, Dec 04, 2025 at 03:11:26PM +0000, Jonathan McDowell wrote:
> On Thu, Dec 04, 2025 at 12:12:11AM +0200, Jarkko Sakkinen wrote:
> > 'name_size' does not have any range checks, and it just directly indexes
> > with TPM_ALG_ID, which could lead into memory corruption at worst.
> >
> > Address the issue by only processing known values and returning -EINVAL for
> > unrecognized values.
> >
> > Make also 'tpm_buf_append_name' and 'tpm_buf_fill_hmac_session' fallible so
> > that errors are detected before causing any spurious TPM traffic.
> >
> > End also the authorization session on failure in both of the functions, as
> > the session state would be then by definition corrupted.
> >
> > Cc: stable@vger.kernel.org # v6.10+
> > Fixes: 1085b8276bb4 ("tpm: Add the rest of the session HMAC API")
> > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
>
> A minor whitespace query below, but:
>
> Reviewed-by: Jonathan McDowell <noodles@meta.com>
Thanks. I updated the commit and removed the extra whitespace.
BR, Jarkko
^ permalink raw reply
* Re: Are setuid shell scripts safe? (Implied by security_bprm_creds_for_exec)
From: Stephen Smalley @ 2025-12-04 15:43 UTC (permalink / raw)
To: Eric W. Biederman
Cc: Roberto Sassu, Bernd Edlinger, Alexander Viro, 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: <87v7iqtcev.fsf_-_@email.froward.int.ebiederm.org>
On Mon, Dec 1, 2025 at 11:34 AM Eric W. Biederman <ebiederm@xmission.com> wrote:
>
> Roberto Sassu <roberto.sassu@huaweicloud.com> writes:
>
> > + Mimi, linux-integrity (would be nice if we are in CC when linux-
> > security-module is in CC).
> >
> > Apologies for not answering earlier, it seems I don't receive the
> > emails from the linux-security-module mailing list (thanks Serge for
> > letting me know!).
> >
> > I see two main effects of this patch. First, the bprm_check_security
> > hook implementations will not see bprm->cred populated. That was a
> > problem before we made this patch:
> >
> > https://patchew.org/linux/20251008113503.2433343-1-roberto.sassu@huaweicloud.com/
>
> Thanks, that is definitely needed.
>
> Does calling process_measurement(CREDS_CHECK) on only the final file
> pass review? Do you know of any cases where that will break things?
>
> As it stands I don't think it should be assumed that any LSM has
> computed it's final creds until bprm_creds_from_file. Not just the
> uid and gid.
>
> If the patch you posted for review works that helps sort that mess out.
>
> > to work around the problem of not calculating the final DAC credentials
> > early enough (well, we actually had to change our CREDS_CHECK hook
> > behavior).
> >
> > The second, I could not check. If I remember well, unlike the
> > capability LSM, SELinux/Apparmor/SMACK calculate the final credentials
> > based on the first file being executed (thus the script, not the
> > interpreter). Is this patch keeping the same behavior despite preparing
> > the credentials when the final binary is found?
>
> The patch I posted was.
>
> My brain is still reeling from the realization that our security modules
> have the implicit assumption that it is safe to calculate their security
> information from shell scripts.
>
> In the first half of the 90's I remember there was lots of effort to try
> and make setuid shell scripts and setuid perl scripts work, and the
> final conclusion was it was a lost cause.
>
> Now I look at security_bprm_creds_for_exec and security_bprm_check which
> both have the implicit assumption that it is indeed safe to compute the
> credentials from a shell script.
>
> When passing a file descriptor to execat we have
> BINPRM_FLAGS_PATH_INACCESSIBLE and use /dev/fd/NNN as the filename
> which reduces some of the races.
>
> However when just plain executing a shell script we pass the filename of
> the shell script as a command line argument, and expect the shell to
> open the filename again. This has been a time of check to time of use
> race for decades, and one of the reasons we don't have setuid shell
> scripts.
>
> Yet the IMA implementation (without the above mentioned patch) assumes
> the final creds will be calculated before security_bprm_check is called,
> and security_bprm_creds_for_exec busily calculate the final creds.
>
> For some of the security modules I believe anyone can set any label they
> want on a file and they remain secure (At which point I don't understand
> the point of having labels on files). I don't believe that is the case
> for selinux, or in general.
>
> So just to remove the TOCTOU race the security_bprm_creds_for_exec
> and security_bprm_check hooks need to be removed, after moving their
> code into something like security_bprm_creds_from_file.
>
> Or am I missing something and even with the TOCTOU race are setuid shell
> scripts somehow safe now?
setuid shell scripts are not safe. But SELinux (and likely AppArmor
and others) have long relied on the ability to transition on shell
scripts to _shed_ permissions. That's a matter of writing your policy
sensibly.
Changing it would break existing userspace and policies.
^ permalink raw reply
* Re: [PATCH v3 1/4] tpm2-sessions: fix out of range indexing in name_size
From: Jonathan McDowell @ 2025-12-04 15:11 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: linux-integrity, Peter Huewe, Jason Gunthorpe, open list, stable,
James Bottomley, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, Ard Biesheuvel,
open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <20251203221215.536031-2-jarkko@kernel.org>
On Thu, Dec 04, 2025 at 12:12:11AM +0200, Jarkko Sakkinen wrote:
>'name_size' does not have any range checks, and it just directly indexes
>with TPM_ALG_ID, which could lead into memory corruption at worst.
>
>Address the issue by only processing known values and returning -EINVAL for
>unrecognized values.
>
>Make also 'tpm_buf_append_name' and 'tpm_buf_fill_hmac_session' fallible so
>that errors are detected before causing any spurious TPM traffic.
>
>End also the authorization session on failure in both of the functions, as
>the session state would be then by definition corrupted.
>
>Cc: stable@vger.kernel.org # v6.10+
>Fixes: 1085b8276bb4 ("tpm: Add the rest of the session HMAC API")
>Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
A minor whitespace query below, but:
Reviewed-by: Jonathan McDowell <noodles@meta.com>
>v3:
>- Fix pr_warn() format string in name_size().
>- Fix !CONFIG_TPM2_HMAC compilation.
>v2:
>- Wrote a better short summary.
>- Addressed remarks in https://lore.kernel.org/linux-integrity/aS8TIeviaippVAha@earth.li/
>- Use -EIO consistently in tpm2_fill_hmac_session. These are not input value
> errors. They could only spun from malformed (kernel) state.
>- name_size did not have a proper default-case. Reorganize the
> fallback into that.
>---
> drivers/char/tpm/tpm2-cmd.c | 23 +++-
> drivers/char/tpm/tpm2-sessions.c | 133 +++++++++++++++-------
> include/linux/tpm.h | 13 ++-
> security/keys/trusted-keys/trusted_tpm2.c | 29 ++++-
> 4 files changed, 143 insertions(+), 55 deletions(-)
>
>diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
>index dd502322f499..be4a9c7f2e1a 100644
>--- a/drivers/char/tpm/tpm2-cmd.c
>+++ b/drivers/char/tpm/tpm2-cmd.c
>@@ -199,7 +199,11 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> }
>
> if (!disable_pcr_integrity) {
>- tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
>+ rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
>+ if (rc) {
>+ tpm_buf_destroy(&buf);
>+ return rc;
>+ }
> tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
> } else {
> tpm_buf_append_handle(chip, &buf, pcr_idx);
>@@ -214,8 +218,14 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> chip->allocated_banks[i].digest_size);
> }
>
>- if (!disable_pcr_integrity)
>- tpm_buf_fill_hmac_session(chip, &buf);
>+ if (!disable_pcr_integrity) {
>+ rc = tpm_buf_fill_hmac_session(chip, &buf);
>+ if (rc) {
>+ tpm_buf_destroy(&buf);
>+ return rc;
>+ }
>+ }
>+
> rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
> if (!disable_pcr_integrity)
> rc = tpm_buf_check_hmac_response(chip, &buf, rc);
>@@ -273,7 +283,12 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> | TPM2_SA_CONTINUE_SESSION,
> NULL, 0);
> tpm_buf_append_u16(&buf, num_bytes);
>- tpm_buf_fill_hmac_session(chip, &buf);
>+ err = tpm_buf_fill_hmac_session(chip, &buf);
>+ if (err) {
>+ tpm_buf_destroy(&buf);
>+ return err;
>+ }
>+
> err = tpm_transmit_cmd(chip, &buf,
> offsetof(struct tpm2_get_random_out,
> buffer),
>diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
>index 6d03c224e6b2..a265e9752a5e 100644
>--- a/drivers/char/tpm/tpm2-sessions.c
>+++ b/drivers/char/tpm/tpm2-sessions.c
>@@ -144,16 +144,23 @@ struct tpm2_auth {
> /*
> * Name Size based on TPM algorithm (assumes no hash bigger than 255)
> */
>-static u8 name_size(const u8 *name)
>+static int name_size(const u8 *name)
> {
>- static u8 size_map[] = {
>- [TPM_ALG_SHA1] = SHA1_DIGEST_SIZE,
>- [TPM_ALG_SHA256] = SHA256_DIGEST_SIZE,
>- [TPM_ALG_SHA384] = SHA384_DIGEST_SIZE,
>- [TPM_ALG_SHA512] = SHA512_DIGEST_SIZE,
>- };
>- u16 alg = get_unaligned_be16(name);
>- return size_map[alg] + 2;
>+ u16 hash_alg = get_unaligned_be16(name);
>+
>+ switch (hash_alg) {
>+ case TPM_ALG_SHA1:
>+ return SHA1_DIGEST_SIZE + 2;
>+ case TPM_ALG_SHA256:
>+ return SHA256_DIGEST_SIZE + 2;
>+ case TPM_ALG_SHA384:
>+ return SHA384_DIGEST_SIZE + 2;
>+ case TPM_ALG_SHA512:
>+ return SHA512_DIGEST_SIZE + 2;
>+ default:
>+ pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg);
>+ return -EINVAL;
>+ }
> }
>
> static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
>@@ -161,6 +168,7 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
> struct tpm_header *head = (struct tpm_header *)buf->data;
> off_t offset = TPM_HEADER_SIZE;
> u32 tot_len = be32_to_cpu(head->length);
>+ int ret;
> u32 val;
>
> /* we're starting after the header so adjust the length */
>@@ -172,9 +180,15 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
> return -EINVAL;
> offset += val;
> /* name */
>+
Spurious extra blank line? Or meant to be before the comment?
> val = tpm_buf_read_u16(buf, &offset);
>- if (val != name_size(&buf->data[offset]))
>+ ret = name_size(&buf->data[offset]);
>+ if (ret < 0)
>+ return ret;
>+
>+ if (val != ret)
> return -EINVAL;
>+
> memcpy(name, &buf->data[offset], val);
> /* forget the rest */
> return 0;
>@@ -221,46 +235,72 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, char *name)
> * As with most tpm_buf operations, success is assumed because failure
> * will be caused by an incorrect programming model and indicated by a
> * kernel message.
>+ *
>+ * Ends the authorization session on failure.
> */
>-void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
>- u32 handle, u8 *name)
>+int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
>+ u32 handle, u8 *name)
> {
> #ifdef CONFIG_TCG_TPM2_HMAC
> enum tpm2_mso_type mso = tpm2_handle_mso(handle);
> struct tpm2_auth *auth;
> int slot;
>+ int ret;
> #endif
>
> if (!tpm2_chip_auth(chip)) {
> tpm_buf_append_handle(chip, buf, handle);
>- return;
>+ return 0;
> }
>
> #ifdef CONFIG_TCG_TPM2_HMAC
> slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
> if (slot >= AUTH_MAX_NAMES) {
>- dev_err(&chip->dev, "TPM: too many handles\n");
>- return;
>+ dev_err(&chip->dev, "too many handles\n");
>+ ret = -EIO;
>+ goto err;
> }
> auth = chip->auth;
>- WARN(auth->session != tpm_buf_length(buf),
>- "name added in wrong place\n");
>+ if (auth->session != tpm_buf_length(buf)) {
>+ dev_err(&chip->dev, "session state malformed");
>+ ret = -EIO;
>+ goto err;
>+ }
> tpm_buf_append_u32(buf, handle);
> auth->session += 4;
>
> if (mso == TPM2_MSO_PERSISTENT ||
> mso == TPM2_MSO_VOLATILE ||
> mso == TPM2_MSO_NVRAM) {
>- if (!name)
>- tpm2_read_public(chip, handle, auth->name[slot]);
>+ if (!name) {
>+ ret = tpm2_read_public(chip, handle, auth->name[slot]);
>+ if (ret)
>+ goto err;
>+ }
> } else {
>- if (name)
>- dev_err(&chip->dev, "TPM: Handle does not require name but one is specified\n");
>+ 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(name));
>+ if (name) {
>+ ret = name_size(name);
>+ if (ret < 0)
>+ goto err;
>+
>+ memcpy(auth->name[slot], name, ret);
>+ }
>+#endif
>+ return 0;
>+
>+#ifdef CONFIG_TCG_TPM2_HMAC
>+err:
>+ tpm2_end_auth_session(chip);
>+ return tpm_ret_to_err(ret);
> #endif
> }
> EXPORT_SYMBOL_GPL(tpm_buf_append_name);
>@@ -533,11 +573,9 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip,
> * encryption key and encrypts the first parameter of the command
> * buffer with it.
> *
>- * As with most tpm_buf operations, success is assumed because failure
>- * will be caused by an incorrect programming model and indicated by a
>- * kernel message.
>+ * Ends the authorization session on failure.
> */
>-void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
>+int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> {
> u32 cc, handles, val;
> struct tpm2_auth *auth = chip->auth;
>@@ -549,9 +587,12 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> u8 cphash[SHA256_DIGEST_SIZE];
> struct sha256_ctx sctx;
> struct hmac_sha256_ctx hctx;
>+ int ret;
>
>- if (!auth)
>- return;
>+ if (!auth) {
>+ ret = -EIO;
>+ goto err;
>+ }
>
> /* save the command code in BE format */
> auth->ordinal = head->ordinal;
>@@ -560,9 +601,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
>
> i = tpm2_find_cc(chip, cc);
> if (i < 0) {
>- dev_err(&chip->dev, "Command 0x%x not found in TPM\n", cc);
>- return;
>+ dev_err(&chip->dev, "command 0x%08x not found\n", cc);
>+ ret = -EIO;
>+ goto err;
> }
>+
> attrs = chip->cc_attrs_tbl[i];
>
> handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
>@@ -576,9 +619,9 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> u32 handle = tpm_buf_read_u32(buf, &offset_s);
>
> if (auth->name_h[i] != handle) {
>- dev_err(&chip->dev, "TPM: handle %d wrong for name\n",
>- i);
>- return;
>+ dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
>+ ret = -EIO;
>+ goto err;
> }
> }
> /* point offset_s to the start of the sessions */
>@@ -609,12 +652,14 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> offset_s += len;
> }
> if (offset_s != offset_p) {
>- dev_err(&chip->dev, "TPM session length is incorrect\n");
>- return;
>+ dev_err(&chip->dev, "session length is incorrect\n");
>+ ret = -EIO;
>+ goto err;
> }
> if (!hmac) {
>- dev_err(&chip->dev, "TPM could not find HMAC session\n");
>- return;
>+ dev_err(&chip->dev, "could not find HMAC session\n");
>+ ret = -EIO;
>+ goto err;
> }
>
> /* encrypt before HMAC */
>@@ -646,8 +691,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> if (mso == TPM2_MSO_PERSISTENT ||
> mso == TPM2_MSO_VOLATILE ||
> mso == TPM2_MSO_NVRAM) {
>- sha256_update(&sctx, auth->name[i],
>- name_size(auth->name[i]));
>+ 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]);
>
>@@ -668,6 +716,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
> hmac_sha256_update(&hctx, &auth->attrs, 1);
> hmac_sha256_final(&hctx, hmac);
>+ return 0;
>+
>+err:
>+ tpm2_end_auth_session(chip);
>+ return ret;
> }
> EXPORT_SYMBOL(tpm_buf_fill_hmac_session);
>
>diff --git a/include/linux/tpm.h b/include/linux/tpm.h
>index 3d8f7d1ce2b8..aa816b144ab3 100644
>--- a/include/linux/tpm.h
>+++ b/include/linux/tpm.h
>@@ -529,8 +529,8 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
> #endif
> }
>
>-void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
>- u32 handle, u8 *name);
>+int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
>+ u32 handle, u8 *name);
> void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
> u8 attributes, u8 *passphrase,
> int passphraselen);
>@@ -563,7 +563,7 @@ static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
> #ifdef CONFIG_TCG_TPM2_HMAC
>
> int tpm2_start_auth_session(struct tpm_chip *chip);
>-void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
>+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);
>@@ -577,10 +577,13 @@ static inline int tpm2_start_auth_session(struct tpm_chip *chip)
> static inline void tpm2_end_auth_session(struct tpm_chip *chip)
> {
> }
>-static inline void tpm_buf_fill_hmac_session(struct tpm_chip *chip,
>- struct tpm_buf *buf)
>+
>+static inline int tpm_buf_fill_hmac_session(struct tpm_chip *chip,
>+ struct tpm_buf *buf)
> {
>+ return 0;
> }
>+
> static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
> struct tpm_buf *buf,
> int rc)
>diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
>index 8bc6efa8accb..5b205279584b 100644
>--- a/security/keys/trusted-keys/trusted_tpm2.c
>+++ b/security/keys/trusted-keys/trusted_tpm2.c
>@@ -268,7 +268,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> goto out_put;
> }
>
>- tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
>+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
>+ if (rc)
>+ goto out;
>+
> tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
> options->keyauth, TPM_DIGEST_SIZE);
>
>@@ -316,7 +319,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> goto out;
> }
>
>- tpm_buf_fill_hmac_session(chip, &buf);
>+ rc = tpm_buf_fill_hmac_session(chip, &buf);
>+ if (rc)
>+ goto out;
>+
> rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data");
> rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> if (rc)
>@@ -427,7 +433,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> return rc;
> }
>
>- tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
>+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
>+ if (rc)
>+ goto out;
>+
> tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
> TPM_DIGEST_SIZE);
>
>@@ -439,7 +448,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> goto out;
> }
>
>- tpm_buf_fill_hmac_session(chip, &buf);
>+ rc = tpm_buf_fill_hmac_session(chip, &buf);
>+ if (rc)
>+ goto out;
>+
> rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob");
> rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> if (!rc)
>@@ -484,7 +496,9 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> return rc;
> }
>
>- tpm_buf_append_name(chip, &buf, blob_handle, NULL);
>+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
>+ if (rc)
>+ goto out;
>
> if (!options->policyhandle) {
> tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
>@@ -509,7 +523,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> NULL, 0);
> }
>
>- tpm_buf_fill_hmac_session(chip, &buf);
>+ rc = tpm_buf_fill_hmac_session(chip, &buf);
>+ if (rc)
>+ goto out;
>+
> rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
> rc = tpm_buf_check_hmac_response(chip, &buf, rc);
>
>--
>2.52.0
>
J.
--
/-\ | Every program is either trivial or
|@/ Debian GNU/Linux Developer | it contains at least one bug.
\- |
^ permalink raw reply
* Re: Are setuid shell scripts safe? (Implied by security_bprm_creds_for_exec)
From: Bernd Edlinger @ 2025-12-04 13:03 UTC (permalink / raw)
To: Al Viro
Cc: 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: <20251204054915.GI1712166@ZenIV>
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.
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...
But with shell scripts an attack is possible, where a sym-link is executed,
and the SUID bit of the target file is used but a race condition might allow
the attacker to replace the script that is used by the shell:
Consider this:
ln -s /usr/bin/legitimate-suid-sctipt.sh
where legitimate-suid-sctipt.sh starts with "#! /bin/bash -"
and the attack works this way:
./legitmate-suid-script.sh &
ln -f -s do-what-i-want.sh legitimate-suid-script.sh
Bernd.
^ 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