Proposal for a design for signed kernel/modules/etc

2017-03-27 Thread Eric McCorkle
Hello everyone,

The following is a design proposal for signed kernel and kernel module
loading, both at boot- and runtime (with the possibility open for signed
executables and libraries if someone wanted to go that route).  I'm
interested in feedback on the idea before I start actually writing code
for it.

== Goals ==

1) Be able to check for a correct cryptographic signature for any kernel
or modules loaded at boot time for some platforms (EFI at a minimum).

2) Be able to check for a correct cryptographic signature for any kernel
module loaded during normal operations (whether or not to do this could
be controlled by a sysctl, securelevel, or some similar mechanism)

3) Work with what's in base already and minimize new additions (ideally,
just a small utility to sign executables)

4) Minimize administrative overhead and ideally, require no changes at
all to maintain signed kernel/modules

5) Have a clear path for supporting signed executables/libraries (I'm
not planning on pursuing this, but it's worth considering that someone
might want to)

6) The design *MUST* support the case where a system builds locally and
uses its own key(s) for signing kernels and modules (and anything else)
and *MUST* allow the administrator complete control over which key(s)
are valid for a given system (ie. no "master keys" controlled by central
organizations)

7) The design must allow for the adoption of new ciphers (there is an
inevitable shift to post-quantum ciphers coming in the near future)

== Non-Goals ==

* Hardware/firmware-based attacks are considered out-of-scope (there is
no viable method for defending against them at the OS level)

* Boot platforms that don't provide their own signature-checking
framework up to loader/kernel can't be properly secured, and are
considered out-of-scope

* Boot platforms that impose size restrictions prohibiting incorporation
of RSA and ED25519 crypto code (ex. i386 BIOS) are considered out-of-scope

* GRUB support is desirable, however it is not necessary to support GRUB
out-of-the-box (meaning a design requiring reasonable modifications to
GRUB is acceptable).

* I am not aiming to support signed executables/libraries now, only to
avoid shutting the door on them.

== Existing Solution(s) ==

EFI has a decent design with regard to key management (platform key,
which signs system keys, which sign the actual loader); however, its
cipher suite is sorely lacking (many broken hashes and weak ciphers, RSA
2048 being the "strongest", no ECC).  It also only works with the COFF
format, and is only available at boot time.  However, it does provide a
chain of custody up to loader (to the extent that anyone trusts
closed-source firmware blobs, SHA1, and 512-2048 bit RSA keys...)  Many
implementations also have master keys "baked in" that would allow
anything signed by random third parties (Microsoft) to boot regardless
of local configurations, or they don't provide any sort of control over
(or even access to) the keys at all.

EFI obviously isn't viable beyond boot time, and misses most of the
goals even there.  Its key management hierarchy is an overall good
design, however.

GRUB currently supports signature checking.  It can be configured to
require signatures for any of its own modules as well as any kernel or
modules that it loads.  These signatures are stored *outside* the
executable/library, in a file with an added .sig extension.  The format
is that of an external signature for the entire ELF file as produced by
the gnupg program.

Linux (I believe) also supports signature checking for modules using the
same convention.

While functional, this design doesn't meet the goals I outlined:

* It relies on the gnupg framework, which is not part of FreeBSD base,
and adding it would be a chore (and would end up duplicating a lot of
functionality provided by OpenSSL)

* It stores the signature separate from the file, which leads to x2 the
number of files, would require modifying existing scripts, and
complicates administrative tasks.  It also leads to failure modes like
stale signatures.

* There are potential legitimate modifications to non-code parts of an
ELF file (such as the .comment section or other similar sections) that
would require re-signing the entire file.

* The previous two problems really start to look bad when you consider
signed executables and libraries, possibly with third-party
build/install scripts...

* Finally, the gnupg signature format doesn't actually seem to be
documented anywhere, or at least not anywhere that doesn't require a lot
of digging...


An alternate solution, which I believe is used in some places is to wrap
the entire executable in a PGP envelope-like format.  This solves the
issue of an external signature file, but would require extensive
modification to the ELF parsing code, let alone the binutils programs
that read/modify ELF files.  This solution also isn't
backwards-compatible at all.  Old loaders/kernels will choke on the
signed libraries.

== Pr

Re: Proposal for a design for signed kernel/modules/etc

2017-03-27 Thread Shawn Webb
Hey Eric,

Thank you for writing this! ELF binary signing has been on my
ever-growing list of things to research and develop. If you'd like help,
please let me know.

I have a few comments, which I've made inline.

On Mon, Mar 27, 2017 at 01:54:44PM -0400, Eric McCorkle wrote:
> Hello everyone,
> 
> The following is a design proposal for signed kernel and kernel module
> loading, both at boot- and runtime (with the possibility open for signed
> executables and libraries if someone wanted to go that route).  I'm
> interested in feedback on the idea before I start actually writing code
> for it.
> 
> == Goals ==
> 
> 1) Be able to check for a correct cryptographic signature for any kernel
> or modules loaded at boot time for some platforms (EFI at a minimum).
> 
> 2) Be able to check for a correct cryptographic signature for any kernel
> module loaded during normal operations (whether or not to do this could
> be controlled by a sysctl, securelevel, or some similar mechanism)
> 
> 3) Work with what's in base already and minimize new additions (ideally,
> just a small utility to sign executables)
> 
> 4) Minimize administrative overhead and ideally, require no changes at
> all to maintain signed kernel/modules
> 
> 5) Have a clear path for supporting signed executables/libraries (I'm
> not planning on pursuing this, but it's worth considering that someone
> might want to)
> 
> 6) The design *MUST* support the case where a system builds locally and
> uses its own key(s) for signing kernels and modules (and anything else)
> and *MUST* allow the administrator complete control over which key(s)
> are valid for a given system (ie. no "master keys" controlled by central
> organizations)
> 
> 7) The design must allow for the adoption of new ciphers (there is an
> inevitable shift to post-quantum ciphers coming in the near future)

As git has shown, having a modular/configurable crypto interface is the
best route. Right now, git is stuck using SHA1 because they didn't
support users being able to choose which hashing algorithm to use.

> 
> == Non-Goals ==
> 
> * Hardware/firmware-based attacks are considered out-of-scope (there is
> no viable method for defending against them at the OS level)
> 
> * Boot platforms that don't provide their own signature-checking
> framework up to loader/kernel can't be properly secured, and are
> considered out-of-scope
> 
> * Boot platforms that impose size restrictions prohibiting incorporation
> of RSA and ED25519 crypto code (ex. i386 BIOS) are considered out-of-scope
> 
> * GRUB support is desirable, however it is not necessary to support GRUB
> out-of-the-box (meaning a design requiring reasonable modifications to
> GRUB is acceptable).
> 
> * I am not aiming to support signed executables/libraries now, only to
> avoid shutting the door on them.

Since FreeBSD uses ELF for the kernel and its modules, it should be
rather straightforward to apply the same work towards userland binaries
and shared libraries.

> 
> == Existing Solution(s) ==
> 
> EFI has a decent design with regard to key management (platform key,
> which signs system keys, which sign the actual loader); however, its
> cipher suite is sorely lacking (many broken hashes and weak ciphers, RSA
> 2048 being the "strongest", no ECC).  It also only works with the COFF
> format, and is only available at boot time.  However, it does provide a
> chain of custody up to loader (to the extent that anyone trusts
> closed-source firmware blobs, SHA1, and 512-2048 bit RSA keys...)  Many
> implementations also have master keys "baked in" that would allow
> anything signed by random third parties (Microsoft) to boot regardless
> of local configurations, or they don't provide any sort of control over
> (or even access to) the keys at all.
> 
> EFI obviously isn't viable beyond boot time, and misses most of the
> goals even there.  Its key management hierarchy is an overall good
> design, however.
> 
> GRUB currently supports signature checking.  It can be configured to
> require signatures for any of its own modules as well as any kernel or
> modules that it loads.  These signatures are stored *outside* the
> executable/library, in a file with an added .sig extension.  The format
> is that of an external signature for the entire ELF file as produced by
> the gnupg program.
> 
> Linux (I believe) also supports signature checking for modules using the
> same convention.
> 
> While functional, this design doesn't meet the goals I outlined:
> 
> * It relies on the gnupg framework, which is not part of FreeBSD base,
> and adding it would be a chore (and would end up duplicating a lot of
> functionality provided by OpenSSL)
> 
> * It stores the signature separate from the file, which leads to x2 the
> number of files, would require modifying existing scripts, and
> complicates administrative tasks.  It also leads to failure modes like
> stale signatures.
> 
> * There are potential legitimate modifications to non-code parts of an
> ELF file

Re: Proposal for a design for signed kernel/modules/etc

2017-03-27 Thread Eric McCorkle
On 03/27/2017 14:37, Shawn Webb wrote:
> Hey Eric,
> 
> Thank you for writing this! ELF binary signing has been on my
> ever-growing list of things to research and develop. If you'd like help,
> please let me know.

I'll probably spin up a branch on my github in the near future.

> As git has shown, having a modular/configurable crypto interface is the
> best route. Right now, git is stuck using SHA1 because they didn't
> support users being able to choose which hashing algorithm to use.

Oh yes!  And all current pubkey crypto has an expiration date probably
between a decade and a century from now.

> You might want to take a look at Microsoft's Authenticode. Microsoft
> made some mistakes early on that allowed attackers to easily trojan
> signed binaries. Your proposal up to this point makes those same
> mistakes. It's been a few years since I researched Authenticode, so I
> don't have any links or documentation handy.
> 
> The conclusion Microsoft came to is that the file as a whole must be
> signed, including offset metadata. Essentially, you'd determine how
> large the .sig section needs to be ahead of time, create it and fill it
> with zeros, then sign the whole file, stuffing the signature in the
> zeroed .sig section. Same concept as calculating checksums of ICMP
> packets.
> 
> This prevents attackers from modifying critical pieces of metadata,
> pointing them to maliciuos payloads. It also prevents attackers from
> appending malicious data to the end of a loadable segment (something
> Authenticode suffered from early on).

Yeah, now that I think about it, there's various forms of evil you could
pull off.  I don't doubt that someone *could* design a scheme that would
defeat all these attacks, but... is it worth it?  Probably not.

Better to just sign the whole file and seal off all the attack vectors,
on second thought.

> Userland shouldn't be trusted to enforce digital signatures. What if
> someone at link time specifies a non-default RTLD? To enforce digital
> signatures of userland binaries/libraries, the ELF image activator
> should be modified to verify the DT_NEEDED entries.

True, I was assuming dlopen just mmaps everything... (I was getting
hungry by that point).  As with the whole-file sigs, it would probably
be better just to have a single syscall that securely loads dynamic
libraries (but that's out-of-scope for now).

> 
> The only other major thing to discuss is supporting public key chaining.
> Ideally, digital signature support should also support chaining multiple
> keys (similar to X.509 PKI). If the accepted solution supported cert
> chaining, then the solution would be more modular. I don't want to go
> down the route of the SSL/TLS PKI mess, but supporting chaining is a
> must in some enterprise environments.
> 
> If we were to support chaining, we could even stuff the pubkey half of
> the key material into another ELF section, so that if a key becomes
> compromised, the old pubkey can be revoked from the trust store and a
> new binary can be generated with new key material. The trusted root
> doesn't need to be cycled as often. HardenedBSD, for example,
> distributes the pubkey that corresponds to the signing privkey inside
> the update tarball for binary updates[1]. This allows us to change key
> material often if desired without the user even noticing.

I can see a two-tiered scheme where you have a "vendor key", which is
used to sign various "application keys", which are used to sign the
kernel and modules.  The signed executables can then supply their
application keys (signed by the vendor key) to the loader/kernel, which
first checks the signature on the key, then on the file.  This way, you
could generate and use a different key for each build, and sign it with
the same vendor key.

There's at least three use cases in play here:

1) FreeBSD would want to publish ready-made installations, probably
signed by a key they control.  This ensures that someone who just wants
to install binary packages and go doesn't load any kernel/modules other
than the ones coming from FreeBSD.

2) I run an enterprise network, and I want to build everything
internally on some build-box, and make sure I only run things that got
built there, or that came from FreeBSD.

3) I have a highly secure network that is set up like in (2), except I
only allow things I build internally to run.

3) I have my laptop, and I want to build everything on the laptop and
make sure I don't boot anything I didn't build.

For all of these, I'd want to bake in at least the vendor key(s).  I
could do this pretty easily by converting the key(s) to a header, then
using it in buildworld/kerrnel.

For (1), FreeBSD would have a vendor key, which it uses to sign the
kernel/modules it publishes, and that would be baked in to those
applications.

For (2), I'd have my own vendor key for my organization, and I'd use
that in addition to FreeBSD's key.  This would allow anything signed by
either of the two organizations.

For (3),