mbox series

[0/3] BPF signature verification

Message ID 20250528215037.2081066-1-bboscaccy@linux.microsoft.com
Headers show
Series BPF signature verification | expand

Message

Blaise Boscaccy May 28, 2025, 9:49 p.m. UTC
As suggested or mandated by KP Singh
https://lore.kernel.org/linux-security-module/CACYkzJ6VQUExfyt0=-FmXz46GHJh3d=FXh5j4KfexcEFbHV-vg@mail.gmail.com/,
this patchset proposes and implements an alternative hash-chain
algorithm for signature verification of BPF programs.

This design diverges in two key ways:

1. Signature Strategy

Two different signature strategies are
implemented. One verifies only the signature of the loader program in
the kernel, as described in the link above. The other verifies the
program’s maps in-kernel via a hash chain.  The original design
required loader programs to be “self-aborting” and embedded the
terminal hash verification logic as metaprogramming code generation
routines inside libbpf. While this patchset supports that scheme, it
is considered undesirable in certain environments due to the potential
for supply-chain attack vectors and the lack of visibility for the LSM
subsystem.  Additionally, it is impossible to verify the code
performing the signature verification, as it is uniquely regenerated
for every program.

2. Timing of Signature Check

This patchset moves the signature check to a point before
security_bpf_prog_load is invoked, due to an unresolved discussion
here:
https://lore.kernel.org/linux-security-module/CAHC9VhTj3=ZXgrYMNA+G64zsOyZO+78uDs1g=kh91=GR5KypYg@mail.gmail.com/
This change allows the LSM subsystem to be informed of the signature
verification result—if it occurred—and the method used, all without
introducing a new hook. It improves visibility and auditability,
reducing the “trust me, friend” aspect of the original design.


Blaise Boscaccy (3):
  bpf: Add bpf_check_signature
  bpf: Support light-skeleton signatures in autogenerated code
  bpftool: Allow signing of light-skeleton programs

 include/linux/bpf.h            |   2 +
 include/linux/verification.h   |   1 +
 include/uapi/linux/bpf.h       |   4 +
 kernel/bpf/arraymap.c          |  11 +-
 kernel/bpf/syscall.c           | 123 +++++++++++++++++++-
 tools/bpf/bpftool/Makefile     |   4 +-
 tools/bpf/bpftool/common.c     | 204 +++++++++++++++++++++++++++++++++
 tools/bpf/bpftool/gen.c        |  66 ++++++++++-
 tools/bpf/bpftool/main.c       |  24 +++-
 tools/bpf/bpftool/main.h       |  23 ++++
 tools/include/uapi/linux/bpf.h |   4 +
 tools/lib/bpf/libbpf.h         |   4 +
 tools/lib/bpf/skel_internal.h  |  28 ++++-
 13 files changed, 491 insertions(+), 7 deletions(-)

Comments

Lukas Wunner May 29, 2025, 10:11 a.m. UTC | #1
On Wed, May 28, 2025 at 02:49:03PM -0700, Blaise Boscaccy wrote:
> +	if (!attr->signature_maps_size) {
> +		sha256((u8 *)prog->insnsi, prog->len * sizeof(struct bpf_insn), (u8 *)&hash);
> +		err = verify_pkcs7_signature(hash, sizeof(hash), signature, attr->signature_size,
> +				     VERIFY_USE_SECONDARY_KEYRING,
> +				     VERIFYING_EBPF_SIGNATURE,
> +				     NULL, NULL);

Has this ever been tested?

It looks like it will always return -EINVAL because:

  verify_pkcs7_signature()
    verify_pkcs7_message_sig()
      pkcs7_verify()

... pkcs7_verify() contains a switch statement which you're not
amending with a "case VERIFYING_EBPF_SIGNATURE" but which returns
-EINVAL in the "default" case.

Aside from that, you may want to consider introducing a new ".ebpf"
keyring to allow adding trusted keys specifically for eBPF verification
without having to rely on the system keyring.

Constraining oneself to sha256 doesn't seem future-proof.

Some minor style issues in the commit message caught my eye:

> This introduces signature verification for eBPF programs inside of the
> bpf subsystem. Two signature validation schemes are included, one that

Use imperative mood, avoid repetitive "This ...", e.g.
"Introduce signature verification of eBPF programs..."

> The signature check is performed before the call to
> security_bpf_prog_load. This allows the LSM subsystem to be clued into
> the result of the signature check, whilst granting knowledge of the
> method and apparatus which was employed.

"Perform the signature check before calling security_bpf_prog_load()
to allow..."

Thanks,

Lukas
Blaise Boscaccy May 29, 2025, 3:32 p.m. UTC | #2
Lukas Wunner <lukas@wunner.de> writes:

> On Wed, May 28, 2025 at 02:49:03PM -0700, Blaise Boscaccy wrote:
>> +	if (!attr->signature_maps_size) {
>> +		sha256((u8 *)prog->insnsi, prog->len * sizeof(struct bpf_insn), (u8 *)&hash);
>> +		err = verify_pkcs7_signature(hash, sizeof(hash), signature, attr->signature_size,
>> +				     VERIFY_USE_SECONDARY_KEYRING,
>> +				     VERIFYING_EBPF_SIGNATURE,
>> +				     NULL, NULL);
>
> Has this ever been tested?
>
> It looks like it will always return -EINVAL because:
>
>   verify_pkcs7_signature()
>     verify_pkcs7_message_sig()
>       pkcs7_verify()
>
> ... pkcs7_verify() contains a switch statement which you're not
> amending with a "case VERIFYING_EBPF_SIGNATURE" but which returns
> -EINVAL in the "default" case.
>

Looks like I missed a commit when sending this patchset. Thanks for
finding that. 

> Aside from that, you may want to consider introducing a new ".ebpf"
> keyring to allow adding trusted keys specifically for eBPF verification
> without having to rely on the system keyring.
>
> Constraining oneself to sha256 doesn't seem future-proof.
>

Definitely not a bad idea, curious, how would you envision that looking
from an UAPI perspective? 

> Some minor style issues in the commit message caught my eye:
>
>> This introduces signature verification for eBPF programs inside of the
>> bpf subsystem. Two signature validation schemes are included, one that
>
> Use imperative mood, avoid repetitive "This ...", e.g.
> "Introduce signature verification of eBPF programs..."
>
>> The signature check is performed before the call to
>> security_bpf_prog_load. This allows the LSM subsystem to be clued into
>> the result of the signature check, whilst granting knowledge of the
>> method and apparatus which was employed.
>
> "Perform the signature check before calling security_bpf_prog_load()
> to allow..."
>
> Thanks,
>
> Lukas
Lukas Wunner May 29, 2025, 7:31 p.m. UTC | #3
On Thu, May 29, 2025 at 08:32:43AM -0700, Blaise Boscaccy wrote:
> Lukas Wunner <lukas@wunner.de> writes:
> > Constraining oneself to sha256 doesn't seem future-proof.
> 
> Definitely not a bad idea, curious, how would you envision that looking
> from an UAPI perspective?

If possible, extend the anonymous struct used by BPF_PROG_LOAD command
with an additional parameter to select the hash algorithm.

Alternatively, create a new command to set the hash algorithm for
subsequent BPF_PROG_LOAD commands.

Use enum hash_algo in include/uapi/linux/hash_info.h to encode the
selected algorithm.  You don't need to support all of these
(some of them are deprecated), but at least the sha3 and possibly
sha2 family is a good idea.

Note that CNSA 2.0 has raised the minimum approved hash size to
384 bits both for sha2 and sha3 in light of PQC:

https://www.fortanix.com/blog/which-post-quantum-cryptography-pqc-algorithm-should-i-use

https://media.defense.gov/2022/Sep/07/2003071836/-1/-1/0/CSI_CNSA_2.0_FAQ_.PDF

Granted, there's no mainline support for PQC signature algorithms yet,
but there's at least one out-of-tree implementation, it's only a question
of when not if something like this is submitted for mainline:

https://github.com/smuellerDD/leancrypto

Thanks,

Lukas
James Bottomley May 29, 2025, 7:36 p.m. UTC | #4
On Thu, 2025-05-29 at 21:31 +0200, Lukas Wunner wrote:
> On Thu, May 29, 2025 at 08:32:43AM -0700, Blaise Boscaccy wrote:
> > Lukas Wunner <lukas@wunner.de> writes:
> > > Constraining oneself to sha256 doesn't seem future-proof.
> > 
> > Definitely not a bad idea, curious, how would you envision that
> > looking from an UAPI perspective?
> 
> If possible, extend the anonymous struct used by BPF_PROG_LOAD
> command with an additional parameter to select the hash algorithm.
> 
> Alternatively, create a new command to set the hash algorithm for
> subsequent BPF_PROG_LOAD commands.

Both of those look like less than good ideas.  There's not much point
having a hash that's different from the hash used in the signature
(which is currently sha256), so we could simply extract the hash from
the PKCS7 bundle and use that.  We can also get bonus points this way
for not modifying any internal APIs ...

Regards,

James
Paul Moore May 30, 2025, 8:14 p.m. UTC | #5
On Fri, May 30, 2025 at 12:42 PM KP Singh <kpsingh@kernel.org> wrote:
> On Wed, May 28, 2025 at 11:50 PM Blaise Boscaccy
> <bboscaccy@linux.microsoft.com> wrote:

...

> Please hold off on further iterations, I am working on a series and
> will share these patches based on the design that was proposed.

I don't think there is any harm in Blaise continuing his work in this
area, especially as he seems to be making reasonable progress towards
a solution that satisfies everyone's needs.  Considering all of the
work that Blaise has already invested in this, and his continued
willingness to try to work with everyone in the community to converge
on a solution, wouldn't it be more beneficial to work with Blaise on
further developing/refining his patchset instead of posting a parallel
effort?  It's your call of course, I'm not going to tell you, or
anyone else, to refrain from posting patches upstream, but it seems
like this is a good opportunity to help foster the development of a
new contributor.

> > 2. Timing of Signature Check
> >
> > This patchset moves the signature check to a point before
> > security_bpf_prog_load is invoked, due to an unresolved discussion
> > here:
>
> This is fine and what I had in mind, signature verification does not
> need to happen in the verifier and the existing hooks are good enough.

Excellent, I'm glad we can agree on the relative placement of the
signature verification and the LSM hook.  Perhaps I misunderstood your
design idea, but I took your comment:

"The signature check in the verifier (during BPF_PROG_LOAD):

 verify_pkcs7_signature(prog->aux->sha, sizeof(prog->aux->sha),
   sig_from_bpf_attr, …);"

https://lore.kernel.org/linux-security-module/CACYkzJ6VQUExfyt0=-FmXz46GHJh3d=FXh5j4KfexcEFbHV-vg@mail.gmail.com/

... to mean that the PKCS7 signature verification was going to happen
*in* the verifier, with the verifier being bpf_check().  Simply for my
own education, if bpf_check() and/or the bpf_check() call in
bpf_prog_load() is not the verifier, it would be helpful to know that,
and also what code is considered the be the BPF verifier.  Regardless,
it's a good step forward that we are all on the same page with respect
to the authorization of signed/unsigned BPF programs.  We still have a
ways to go it looks like, but we're making good progress.
Blaise Boscaccy May 30, 2025, 9:19 p.m. UTC | #6
KP Singh <kpsingh@kernel.org> writes:

> On Wed, May 28, 2025 at 11:50 PM Blaise Boscaccy
> <bboscaccy@linux.microsoft.com> wrote:
>>
>> As suggested or mandated by KP Singh
>> https://lore.kernel.org/linux-security-module/CACYkzJ6VQUExfyt0=-FmXz46GHJh3d=FXh5j4KfexcEFbHV-vg@mail.gmail.com/,
>> this patchset proposes and implements an alternative hash-chain
>> algorithm for signature verification of BPF programs.
>>
>>
>>
>> This design diverges in two key ways:
>>
>> 1. Signature Strategy
>>
>> Two different signature strategies are
>> implemented. One verifies only the signature of the loader program in
>> the kernel, as described in the link above. The other verifies the
>> program’s maps in-kernel via a hash chain.  The original design
>> required loader programs to be “self-aborting” and embedded the
>> terminal hash verification logic as metaprogramming code generation
>> routines inside libbpf. While this patchset supports that scheme, it
>> is considered undesirable in certain environments due to the potential
>> for supply-chain attack vectors and the lack of visibility for the LSM
>
> The loader program is signed by a trusted entity, If you trust the
> signature, then you trust it to do the signature verification.

That's the whole point. I explicitly don't want to be forced, by you,
to trust unspecified third parties, BPF programs or the BPF virtual
machine/JIT to perform signature verification, when it's demonstrably
trivial to do this in the kernel, without precluding or limiting the
chain loader scheme that you wish to have for Cilium/bpftrace.

> This is
> a fairly common pattern in security and a pattern that we will be
> using in other signed bpf use-cases which can choose to depend on
> signed loaders.
>

And that isn't at odds with the kernel being able to do it nor is it
with what I posted.

> If your build environment that signs the BPF program is compromised
> and can inject arbitrary code, then signing does not help.  Can you
> explain what a supply chain attack would look like here?
>

Most people here can read C code. The number of people that can read
ebpf assembly metaprogramming code is much smaller. Compromising clang
is one thing, compromising libbpf is another. Your proposal increases
the attack surface with no observable benefit. If I was going to leave a
hard-to-find backdoor into ring0, gen.c would be a fun place to explore
doing it. Module and UEFI signature verification code doesn't live
inside of GCC or Clang as set of meta-instructions that get emitted, and
there are very good reasons for that.

Further, since the signature verification code is unique for each and
every program it needs to be verified/proved/tested for each and every
program. Additionally, since all these checks are being forced outside
of the kernel proper, with the insistence of keeping the LSM layer in
the dark of the ultimate result, the only way to test that a program
will fail if the map is corrupted is to physically corrupt each and
every program and test that individually. That isn't "elegant" nor "user
friendly" in any way, shape or form.

>> subsystem.  Additionally, it is impossible to verify the code
>> performing the signature verification, as it is uniquely regenerated
>
> The LSM needs to ensure that it allows trusted LOADER programs i.e.
> with signatures and potentially trusted signed user-space binaries
> with unsigned or delegated signing (this will be needed for Cilium and
> bpftrace that dynamically generate BPF programs), that's a more
> important aspect of the LSM policy from a BPF perspective.
>

I would like to be able to sign my programs please and have the kernel
verify it was done correctly. Why are you insisting that I *don't* do
that?  I'm yet to see any technical objection to doing that. Do you have
one that you'd like to share at this point?

> MAP_EXCLUSIVE is missing and is required which prevents maps from
> being accessed by other programs as explained in the proposal.
>
> Please hold off on further iterations, I am working on a series and
> will share these patches based on the design that was proposed.
>

So the premise here seems to be that people should only be allowed to
sign trusted loaders, and that trusted loaders must additionally be
authored by you, correct?

When can we expect to see your patchset posted?

>>
>> for every program.
>>
>>
>>
>> 2. Timing of Signature Check
>>
>> This patchset moves the signature check to a point before
>> security_bpf_prog_load is invoked, due to an unresolved discussion
>> here:
>
> This is fine and what I had in mind, signature verification does not
> need to happen in the verifier and the existing hooks are good enough.
> I did not reply to Paul's comment since this is a fairly trivial
> detail and would be obvious in the implementation that the verifier is
> not the right place to check the signature anyways as the instruction
> buffer is only stable pre-verification.
>
>> https://lore.kernel.org/linux-security-module/CAHC9VhTj3=ZXgrYMNA+G64zsOyZO+78uDs1g=kh91=GR5KypYg@mail.gmail.com/
>> This change allows the LSM subsystem to be informed of the signature
>> verification result—if it occurred—and the method used, all without
>> introducing a new hook. It improves visibility and auditability,
>> reducing the “trust me, friend” aspect of the original design.
>
>
> On Wed, May 28, 2025 at 11:50 PM Blaise Boscaccy
> <bboscaccy@linux.microsoft.com> wrote:
>>
>> As suggested or mandated by KP Singh
>> https://lore.kernel.org/linux-security-module/CACYkzJ6VQUExfyt0=-FmXz46GHJh3d=FXh5j4KfexcEFbHV-vg@mail.gmail.com/,
>> this patchset proposes and implements an alternative hash-chain
>> algorithm for signature verification of BPF programs.
>>
>> This design diverges in two key ways:
>>
>> 1. Signature Strategy
>>
>> Two different signature strategies are
>> implemented. One verifies only the signature of the loader program in
>> the kernel, as described in the link above. The other verifies the
>> program’s maps in-kernel via a hash chain.  The original design
>> required loader programs to be “self-aborting” and embedded the
>> terminal hash verification logic as metaprogramming code generation
>> routines inside libbpf. While this patchset supports that scheme, it
>> is considered undesirable in certain environments due to the potential
>> for supply-chain attack vectors and the lack of visibility for the LSM
>> subsystem.  Additionally, it is impossible to verify the code
>> performing the signature verification, as it is uniquely regenerated
>> for every program.
>>
>> 2. Timing of Signature Check
>>
>> This patchset moves the signature check to a point before
>> security_bpf_prog_load is invoked, due to an unresolved discussion
>> here:
>> https://lore.kernel.org/linux-security-module/CAHC9VhTj3=ZXgrYMNA+G64zsOyZO+78uDs1g=kh91=GR5KypYg@mail.gmail.com/
>> This change allows the LSM subsystem to be informed of the signature
>> verification result—if it occurred—and the method used, all without
>> introducing a new hook. It improves visibility and auditability,
>> reducing the “trust me, friend” aspect of the original design.
>>
>>
>> Blaise Boscaccy (3):
>>   bpf: Add bpf_check_signature
>>   bpf: Support light-skeleton signatures in autogenerated code
>>   bpftool: Allow signing of light-skeleton programs
>>
>>  include/linux/bpf.h            |   2 +
>>  include/linux/verification.h   |   1 +
>>  include/uapi/linux/bpf.h       |   4 +
>>  kernel/bpf/arraymap.c          |  11 +-
>>  kernel/bpf/syscall.c           | 123 +++++++++++++++++++-
>>  tools/bpf/bpftool/Makefile     |   4 +-
>>  tools/bpf/bpftool/common.c     | 204 +++++++++++++++++++++++++++++++++
>>  tools/bpf/bpftool/gen.c        |  66 ++++++++++-
>>  tools/bpf/bpftool/main.c       |  24 +++-
>>  tools/bpf/bpftool/main.h       |  23 ++++
>>  tools/include/uapi/linux/bpf.h |   4 +
>>  tools/lib/bpf/libbpf.h         |   4 +
>>  tools/lib/bpf/skel_internal.h  |  28 ++++-
>>  13 files changed, 491 insertions(+), 7 deletions(-)
>>
>> --
>> 2.48.1
>>
KP Singh May 30, 2025, 9:33 p.m. UTC | #7
On Fri, May 30, 2025 at 11:32 PM KP Singh <kpsingh@kernel.org> wrote:
>
> On Fri, May 30, 2025 at 11:19 PM Blaise Boscaccy
> <bboscaccy@linux.microsoft.com> wrote:
> >
> > KP Singh <kpsingh@kernel.org> writes:
> >
>
> [...]
>
> > >
> >
> > And that isn't at odds with the kernel being able to do it nor is it
> > with what I posted.
> >
> > > If your build environment that signs the BPF program is compromised
> > > and can inject arbitrary code, then signing does not help.  Can you
> > > explain what a supply chain attack would look like here?
> > >
> >
> > Most people here can read C code. The number of people that can read
> > ebpf assembly metaprogramming code is much smaller. Compromising clang
> > is one thing, compromising libbpf is another. Your proposal increases
> > the attack surface with no observable benefit. If I was going to leave a
> > hard-to-find backdoor into ring0, gen.c would be a fun place to explore
> > doing it. Module and UEFI signature verification code doesn't live
> > inside of GCC or Clang as set of meta-instructions that get emitted, and
> > there are very good reasons for that.
> >
> > Further, since the signature verification code is unique for each and
> > every program it needs to be verified/proved/tested for each and every
> > program. Additionally, since all these checks are being forced outside
> > of the kernel proper, with the insistence of keeping the LSM layer in
> > the dark of the ultimate result, the only way to test that a program
> > will fail if the map is corrupted is to physically corrupt each and
> > every program and test that individually. That isn't "elegant" nor "user
> > friendly" in any way, shape or form.
> >
> > >> subsystem.  Additionally, it is impossible to verify the code
> > >> performing the signature verification, as it is uniquely regenerated
> > >
> > > The LSM needs to ensure that it allows trusted LOADER programs i.e.
> > > with signatures and potentially trusted signed user-space binaries
> > > with unsigned or delegated signing (this will be needed for Cilium and
> > > bpftrace that dynamically generate BPF programs), that's a more
> > > important aspect of the LSM policy from a BPF perspective.
> > >
> >
> > I would like to be able to sign my programs please and have the kernel
> > verify it was done correctly. Why are you insisting that I *don't* do
> > that?  I'm yet to see any technical objection to doing that. Do you have
> > one that you'd like to share at this point?
>
> The kernel allows a trusted loader that's signed with your private
> key, that runs in the kernel context to delegate the verification.
> This pattern of a trusted / delegated loader is going to be required
> for many of the BPF use-cases that are out there (Cilium, bpftrace)
> that dynamically generate eBPF programs.
>
> The technical objection is that:
>
> * It does not align with most BPF use-cases out there as most
> use-cases need a trusted loader.
> * Locks us into a UAPI, whereas a signed LOADER allows us to
> incrementally build signing for all use-cases without compromising the
> security properties.
>
> BPF's philosophy is that of flexibility and not locking the users into
> a rigid in-kernel implementation and UAPI.
>
> - KP
>
> >
> > > MAP_EXCLUSIVE is missing and is required which prevents maps from
> > > being accessed by other programs as explained in the proposal.
> > >
> > > Please hold off on further iterations, I am working on a series and
> > > will share these patches based on the design that was proposed.
> > >
> >
> > So the premise here seems to be that people should only be allowed to
> > sign trusted loaders, and that trusted loaders must additionally be
> > authored by you, correct?
> >
> > When can we expect to see your patchset posted?

I will try to get this out by the end of next week.

- KP

> >
Blaise Boscaccy May 30, 2025, 10:15 p.m. UTC | #8
KP Singh <kpsingh@kernel.org> writes:

> On Fri, May 30, 2025 at 11:32 PM KP Singh <kpsingh@kernel.org> wrote:
>>
>> On Fri, May 30, 2025 at 11:19 PM Blaise Boscaccy
>> <bboscaccy@linux.microsoft.com> wrote:
>> >
>> > KP Singh <kpsingh@kernel.org> writes:
>> >
>>
>> [...]
>>
>> > >
>> >
>> > And that isn't at odds with the kernel being able to do it nor is it
>> > with what I posted.
>> >
>> > > If your build environment that signs the BPF program is compromised
>> > > and can inject arbitrary code, then signing does not help.  Can you
>> > > explain what a supply chain attack would look like here?
>> > >
>> >
>> > Most people here can read C code. The number of people that can read
>> > ebpf assembly metaprogramming code is much smaller. Compromising clang
>> > is one thing, compromising libbpf is another. Your proposal increases
>> > the attack surface with no observable benefit. If I was going to leave a
>> > hard-to-find backdoor into ring0, gen.c would be a fun place to explore
>> > doing it. Module and UEFI signature verification code doesn't live
>> > inside of GCC or Clang as set of meta-instructions that get emitted, and
>> > there are very good reasons for that.
>> >
>> > Further, since the signature verification code is unique for each and
>> > every program it needs to be verified/proved/tested for each and every
>> > program. Additionally, since all these checks are being forced outside
>> > of the kernel proper, with the insistence of keeping the LSM layer in
>> > the dark of the ultimate result, the only way to test that a program
>> > will fail if the map is corrupted is to physically corrupt each and
>> > every program and test that individually. That isn't "elegant" nor "user
>> > friendly" in any way, shape or form.
>> >
>> > >> subsystem.  Additionally, it is impossible to verify the code
>> > >> performing the signature verification, as it is uniquely regenerated
>> > >
>> > > The LSM needs to ensure that it allows trusted LOADER programs i.e.
>> > > with signatures and potentially trusted signed user-space binaries
>> > > with unsigned or delegated signing (this will be needed for Cilium and
>> > > bpftrace that dynamically generate BPF programs), that's a more
>> > > important aspect of the LSM policy from a BPF perspective.
>> > >
>> >
>> > I would like to be able to sign my programs please and have the kernel
>> > verify it was done correctly. Why are you insisting that I *don't* do
>> > that?  I'm yet to see any technical objection to doing that. Do you have
>> > one that you'd like to share at this point?
>>
>> The kernel allows a trusted loader that's signed with your private
>> key, that runs in the kernel context to delegate the verification.
>> This pattern of a trusted / delegated loader is going to be required
>> for many of the BPF use-cases that are out there (Cilium, bpftrace)
>> that dynamically generate eBPF programs.
>>
>> The technical objection is that:
>>
>> * It does not align with most BPF use-cases out there as most
>> use-cases need a trusted loader.
>> * Locks us into a UAPI, whereas a signed LOADER allows us to
>> incrementally build signing for all use-cases without compromising the
>> security properties.
>>
>> BPF's philosophy is that of flexibility and not locking the users into
>> a rigid in-kernel implementation and UAPI.
>>
>> - KP
>>
>> >
>> > > MAP_EXCLUSIVE is missing and is required which prevents maps from
>> > > being accessed by other programs as explained in the proposal.
>> > >
>> > > Please hold off on further iterations, I am working on a series and
>> > > will share these patches based on the design that was proposed.
>> > >
>> >
>> > So the premise here seems to be that people should only be allowed to
>> > sign trusted loaders, and that trusted loaders must additionally be
>> > authored by you, correct?
>> >
>> > When can we expect to see your patchset posted?
>
> I will try to get this out by the end of next week.

Wonderful, we look forward to seeing your patchset.

-blaise

>
> - KP
>
>> >
Blaise Boscaccy May 30, 2025, 10:27 p.m. UTC | #9
KP Singh <kpsingh@kernel.org> writes:

> On Sat, May 31, 2025 at 12:14 AM Blaise Boscaccy
> <bboscaccy@linux.microsoft.com> wrote:
>>
>> KP Singh <kpsingh@kernel.org> writes:
>>
>> > On Fri, May 30, 2025 at 11:19 PM Blaise Boscaccy
>> > <bboscaccy@linux.microsoft.com> wrote:
>> >>
>> >> KP Singh <kpsingh@kernel.org> writes:
>> >>
>> >
>> > [...]
>> >
>> >> >
>> >>
>> >> And that isn't at odds with the kernel being able to do it nor is it
>> >> with what I posted.
>> >>
>> >> > If your build environment that signs the BPF program is compromised
>> >> > and can inject arbitrary code, then signing does not help.  Can you
>> >> > explain what a supply chain attack would look like here?
>> >> >
>> >>
>> >> Most people here can read C code. The number of people that can read
>> >> ebpf assembly metaprogramming code is much smaller. Compromising clang
>> >> is one thing, compromising libbpf is another. Your proposal increases
>> >> the attack surface with no observable benefit. If I was going to leave a
>> >> hard-to-find backdoor into ring0, gen.c would be a fun place to explore
>> >> doing it. Module and UEFI signature verification code doesn't live
>> >> inside of GCC or Clang as set of meta-instructions that get emitted, and
>> >> there are very good reasons for that.
>> >>
>> >> Further, since the signature verification code is unique for each and
>> >> every program it needs to be verified/proved/tested for each and every
>> >> program. Additionally, since all these checks are being forced outside
>> >> of the kernel proper, with the insistence of keeping the LSM layer in
>> >> the dark of the ultimate result, the only way to test that a program
>> >> will fail if the map is corrupted is to physically corrupt each and
>> >> every program and test that individually. That isn't "elegant" nor "user
>> >> friendly" in any way, shape or form.
>> >>
>> >> >> subsystem.  Additionally, it is impossible to verify the code
>> >> >> performing the signature verification, as it is uniquely regenerated
>> >> >
>> >> > The LSM needs to ensure that it allows trusted LOADER programs i.e.
>> >> > with signatures and potentially trusted signed user-space binaries
>> >> > with unsigned or delegated signing (this will be needed for Cilium and
>> >> > bpftrace that dynamically generate BPF programs), that's a more
>> >> > important aspect of the LSM policy from a BPF perspective.
>> >> >
>> >>
>> >> I would like to be able to sign my programs please and have the kernel
>> >> verify it was done correctly. Why are you insisting that I *don't* do
>> >> that?  I'm yet to see any technical objection to doing that. Do you have
>> >> one that you'd like to share at this point?
>> >
>> > The kernel allows a trusted loader that's signed with your private
>> > key, that runs in the kernel context to delegate the verification.
>> > This pattern of a trusted / delegated loader is going to be required
>> > for many of the BPF use-cases that are out there (Cilium, bpftrace)
>> > that dynamically generate eBPF programs.
>> >
>> > The technical objection is that:
>> >
>> > * It does not align with most BPF use-cases out there as most
>> > use-cases need a trusted loader.
>>
>> No, it's definitely a use case. It's trivial to support both a trusted
>> loader and a signature over the hash chain of supplied assets.
>>
>> > * Locks us into a UAPI, whereas a signed LOADER allows us to
>> > incrementally build signing for all use-cases without compromising the
>> > security properties.
>> >
>>
>> Your proposal locks us into a UAPI as well. There is no way to make to
>> do this via UAPI without making a UAPI design choice.
>>
>> > BPF's philosophy is that of flexibility and not locking the users into
>> > a rigid in-kernel implementation and UAPI.
>> >
>>
>> Then why are you locking us into a rigid
>> only-signing-the-loader-is-allowed implementation?
>
> I explained this before, the delegated / trusted loader is needed by
> many BPF use-cases. A UAPI is forever, thus the lock-in.
>

Again, I'm not following. What is technically wrong with supporting both
signing a loader only and allowing for the signature of multiple
passed-in assets? It's trivial to support both and any path forward will
force a UAPI lock-in.

Do you simply feel that it isn't a valid use case and therefore we
shouldn't be allowed to do it?

> - KP
>
>>
>> > - KP
>> >
>> >>
>> >> > MAP_EXCLUSIVE is missing and is required which prevents maps from
>> >> > being accessed by other programs as explained in the proposal.
>> >> >
>> >> > Please hold off on further iterations, I am working on a series and
>> >> > will share these patches based on the design that was proposed.
>> >> >
>> >>
>> >> So the premise here seems to be that people should only be allowed to
>> >> sign trusted loaders, and that trusted loaders must additionally be
>> >> authored by you, correct?
>> >>
>> >> When can we expect to see your patchset posted?
>> >>
>> >> >>
>> >> >> for every program.
>> >> >>
>> >> >>
>> >> >>
>> >> >> 2. Timing of Signature Check
>> >> >>
>> >> >> This patchset moves the signature check to a point before
>> >> >> security_bpf_prog_load is invoked, due to an unresolved discussion
>> >> >> here:
>> >> >
>> >> > This is fine and what I had in mind, signature verification does not
>> >> > need to happen in the verifier and the existing hooks are good enough.
>> >> > I did not reply to Paul's comment since this is a fairly trivial
>> >> > detail and would be obvious in the implementation that the verifier is
>> >> > not the right place to check the signature anyways as the instruction
>> >> > buffer is only stable pre-verification.
>> >> >
>> >> >> https://lore.kernel.org/linux-security-module/CAHC9VhTj3=ZXgrYMNA+G64zsOyZO+78uDs1g=kh91=GR5KypYg@mail.gmail.com/
>> >> >> This change allows the LSM subsystem to be informed of the signature
>> >> >> verification result—if it occurred—and the method used, all without
>> >> >> introducing a new hook. It improves visibility and auditability,
>> >> >> reducing the “trust me, friend” aspect of the original design.
>> >> >
>> >> >
>> >> > On Wed, May 28, 2025 at 11:50 PM Blaise Boscaccy
>> >> > <bboscaccy@linux.microsoft.com> wrote:
>> >> >>
>> >> >> As suggested or mandated by KP Singh
>> >> >> https://lore.kernel.org/linux-security-module/CACYkzJ6VQUExfyt0=-FmXz46GHJh3d=FXh5j4KfexcEFbHV-vg@mail.gmail.com/,
>> >> >> this patchset proposes and implements an alternative hash-chain
>> >> >> algorithm for signature verification of BPF programs.
>> >> >>
>> >> >> This design diverges in two key ways:
>> >> >>
>> >> >> 1. Signature Strategy
>> >> >>
>> >> >> Two different signature strategies are
>> >> >> implemented. One verifies only the signature of the loader program in
>> >> >> the kernel, as described in the link above. The other verifies the
>> >> >> program’s maps in-kernel via a hash chain.  The original design
>> >> >> required loader programs to be “self-aborting” and embedded the
>> >> >> terminal hash verification logic as metaprogramming code generation
>> >> >> routines inside libbpf. While this patchset supports that scheme, it
>> >> >> is considered undesirable in certain environments due to the potential
>> >> >> for supply-chain attack vectors and the lack of visibility for the LSM
>> >> >> subsystem.  Additionally, it is impossible to verify the code
>> >> >> performing the signature verification, as it is uniquely regenerated
>> >> >> for every program.
>> >> >>
>> >> >> 2. Timing of Signature Check
>> >> >>
>> >> >> This patchset moves the signature check to a point before
>> >> >> security_bpf_prog_load is invoked, due to an unresolved discussion
>> >> >> here:
>> >> >> https://lore.kernel.org/linux-security-module/CAHC9VhTj3=ZXgrYMNA+G64zsOyZO+78uDs1g=kh91=GR5KypYg@mail.gmail.com/
>> >> >> This change allows the LSM subsystem to be informed of the signature
>> >> >> verification result—if it occurred—and the method used, all without
>> >> >> introducing a new hook. It improves visibility and auditability,
>> >> >> reducing the “trust me, friend” aspect of the original design.
>> >> >>
>> >> >>
>> >> >> Blaise Boscaccy (3):
>> >> >>   bpf: Add bpf_check_signature
>> >> >>   bpf: Support light-skeleton signatures in autogenerated code
>> >> >>   bpftool: Allow signing of light-skeleton programs
>> >> >>
>> >> >>  include/linux/bpf.h            |   2 +
>> >> >>  include/linux/verification.h   |   1 +
>> >> >>  include/uapi/linux/bpf.h       |   4 +
>> >> >>  kernel/bpf/arraymap.c          |  11 +-
>> >> >>  kernel/bpf/syscall.c           | 123 +++++++++++++++++++-
>> >> >>  tools/bpf/bpftool/Makefile     |   4 +-
>> >> >>  tools/bpf/bpftool/common.c     | 204 +++++++++++++++++++++++++++++++++
>> >> >>  tools/bpf/bpftool/gen.c        |  66 ++++++++++-
>> >> >>  tools/bpf/bpftool/main.c       |  24 +++-
>> >> >>  tools/bpf/bpftool/main.h       |  23 ++++
>> >> >>  tools/include/uapi/linux/bpf.h |   4 +
>> >> >>  tools/lib/bpf/libbpf.h         |   4 +
>> >> >>  tools/lib/bpf/skel_internal.h  |  28 ++++-
>> >> >>  13 files changed, 491 insertions(+), 7 deletions(-)
>> >> >>
>> >> >> --
>> >> >> 2.48.1
>> >> >>
KP Singh May 30, 2025, 11:32 p.m. UTC | #10
> And I'm saying that they are, based on wanting visibility in the LSM
> layer, passing that along to the end user, and wanting to be able to
> show correctness, along with mitigating an entire vector of supply chain
> attacks targeting gen.c.

What supply chain attack?I asked this earlier, you never replied, what
does a supply chain attack here really look like?


- KP

>
> So in summary, your objection to this is that you feel it's simply "not
> needed", and those above risks/design problems aren't actually an issue?
>
> > Let's have this discussion in the patch series, much easier to discuss
> > with the code.
>
> I think we've all been waiting for that. Yes, lets.