Re: [PATCH RFC 00/12] Enroll kernel keys thru MOK

From: Eric Snowberg
Date: Thu Jul 08 2021 - 14:00:52 EST



> On Jul 8, 2021, at 7:56 AM, Mimi Zohar <zohar@xxxxxxxxxxxxx> wrote:
>
> On Wed, 2021-07-07 at 16:10 -0600, Eric Snowberg wrote:
>>> On Jul 7, 2021, at 11:00 AM, Mimi Zohar <zohar@xxxxxxxxxxxxx> wrote:
>>>
>>> On Wed, 2021-07-07 at 10:28 -0600, Eric Snowberg wrote:
>>>>> On Jul 7, 2021, at 6:39 AM, Mimi Zohar <zohar@xxxxxxxxxxxxx> wrote:
>>>>>
>>>>> On Tue, 2021-07-06 at 22:43 -0400, Eric Snowberg wrote:
>>>>>> This is a follow up to the "Add additional MOK vars" [1] series I
>>>>>> previously sent. This series incorporates the feedback given
>>>>>> both publicly on the mailing list and privately from Mimi. This
>>>>>> series just focuses on getting end-user keys into the kernel trust
>>>>>> boundary.
>>>>>>
>>>>>> Currently, pre-boot keys are not trusted within the Linux boundary [2].
>>>>>> Pre-boot keys include UEFI Secure Boot DB keys and MOKList keys. These
>>>>>> keys are loaded into the platform keyring and can only be used for kexec.
>>>>>> If an end-user wants to use their own key within the Linux trust
>>>>>> boundary, they must either compile it into the kernel themselves or use
>>>>>> the insert-sys-cert script. Both options present a problem. Many
>>>>>> end-users do not want to compile their own kernels. With the
>>>>>> insert-sys-cert option, there are missing upstream changes [3]. Also,
>>>>>> with the insert-sys-cert option, the end-user must re-sign their kernel
>>>>>> again with their own key, and then insert that key into the MOK db.
>>>>>> Another problem with insert-sys-cert is that only a single key can be
>>>>>> inserted into a compressed kernel.
>>>>>>
>>>>>> Having the ability to insert a key into the Linux trust boundary opens
>>>>>> up various possibilities. The end-user can use a pre-built kernel and
>>>>>> sign their own kernel modules. It also opens up the ability for an
>>>>>> end-user to more easily use digital signature based IMA-appraisal. To
>>>>>> get a key into the ima keyring, it must be signed by a key within the
>>>>>> Linux trust boundary.
>>>>>>
>>>>>> Downstream Linux distros try to have a single signed kernel for each
>>>>>> architecture. Each end-user may use this kernel in entirely different
>>>>>> ways. Some downstream kernels have chosen to always trust platform keys
>>>>>> within the Linux trust boundary for kernel module signing. These
>>>>>> kernels have no way of using digital signature base IMA appraisal.
>>>>>>
>>>>>> This series adds a new MOK variable to shim. This variable allows the
>>>>>> end-user to decide if they want to trust keys enrolled in the MOK within
>>>>>> the Linux trust boundary. By default, nothing changes; MOK keys are
>>>>>> not trusted within the Linux kernel. They are only trusted after the
>>>>>> end-user makes the decision themselves. The end-user would set this
>>>>>> through mokutil using a new --trust-mok option [4]. This would work
>>>>>> similar to how the kernel uses MOK variable to enable/disable signature
>>>>>> validation as well as use/ignore the db.
>>>>>>
>>>>>> When shim boots, it mirrors the new MokTML Boot Services variable to a new
>>>>>> MokListTrustedRT Runtime Services variable and extends PCR14.
>>>>>> MokListTrustedRT is written without EFI_VARIABLE_NON_VOLATILE set,
>>>>>> preventing an end-user from setting it after booting and doing a kexec.
>>>>>>
>>>>>> When the kernel boots, if MokListTrustedRT is set and
>>>>>> EFI_VARIABLE_NON_VOLATILE is not set, the MokListRT is loaded into the
>>>>>> secondary trusted keyring instead of the platform keyring. Mimi has
>>>>>> suggested that only CA keys or keys that can be vouched for by other
>>>>>> kernel keys be loaded. All other certs will load into the platform
>>>>>> keyring instead.
>>>>>
>>>>> Loading MOK CA keys onto the "secondary" keyring would need to be an
>>>>> exception. Once CA keys are loaded onto the "secondary" keyring, any
>>>>> certificates signed by those CA keys may be loaded normally, without
>>>>> needing an exception, onto the "secondary" keyring. The kernel MAY
>>>>> load those keys onto the "secondary" keyring, but really doesn't need
>>>>> to be involved.
>>>>>
>>>>> Loading ALL of the MOK db keys onto either the "secondary" or
>>>>> "platform" keyrings makes the code a lot more complicated. Is it
>>>>> really necessary?
>>>>
>>>> Today all keys are loaded into the platform keyring. For kexec_file_load,
>>>> the platform and secondary keys are trusted the same. If this series were
>>>> not to load them all into either keyring, it would be a kexec_file_load
>>>> regression, since keys that previously loaded into the platform keyring
>>>> could be missing. The complexity arises from the CA key restriction.
>>>> If that requirement was removed, this series would be much smaller.
>>>
>>> To prevent the regression, allow the the existing firmware/UEFI keys to
>>> continue to be loaded on the platform keyring, as it is currently being
>>> done. The new code would load just the MOK db CA keys onto the
>>> secondary keyring, based on the new UEFI variable. This is the only
>>> code that would require a
>>> "restrict_link_by_builtin_and_secondary_trusted" exemption. The code
>>> duplication would be minimal in comparison to the complexity being
>>> introduced.
>>
>> This series was written with the following three requirements in mind:
>>
>> 1. Only CA keys that were originally bound for the platform keyring
>> can enter the secondary keyring.
>>
>> 2. No key in the UEFI Secure Boot DB, CA or not, may enter the
>> secondary keyring, only MOKList keys may be trusted.
>>
>> 3. A new MOK variable is added to signify the user wants to trust
>> MOKList keys.
>
> Sounds good!
>
>>
>> Given these requirements, I started down the path I think you are
>> suggesting. However I found it to be more complex. If we load all
>> keys into the platform keyring first and later try to load only CA keys,
>> we don’t have a way of knowing where the platform key came from.
>> Platform keys can originate from the UEFI Secure Boot DB or the MOKList.
>> This would violate the second requirement. This caused me to need to
>> create a new keyring handler. [PATCH RFC 10/12] integrity: add new
>> keyring handler.
>
> To prevent the regression you mentioned, I was suggesting reading the
> MOK DB twice. One time loading all the keys onto the platform keyring.
> The other time loading only the CA keys onto the secondary keyring.
>
>>
>> To satisfy the first requirement a new restriction is required. This
>> is contained in [PATCH RFC 03/12] KEYS: CA link restriction.
>>
>> To satisfy the third requirement, we must read the new MOK var. This
>> is contained in [PATCH RFC 06/12] integrity: Trust mok keys if
>> MokListTrustedRT found.
>>
>> The patches above make up a majority of the new code.
>>
>> The remaining code of creating a new .mok keyring was done with code
>> reuse in mind. Many of the required functions necessary to add this
>> capability is already contained in integrity_ functions. If the
>> operation was done directly on the secondary keyring, similar code
>> would need to be added to certs/system_keyring.c. Just like how the
>> platform keyring is created within integrity code, the mok keyring
>> is created in the same fashion. When the platform keyring has
>> completed initialization and loaded all its keys, the keyring is set
>> into system_keyring code using set_platform_trusted_keys. Instead of
>> setting the mok keyring, I’m moving the keys directly into the secondary
>> keyring, while bypassing the current restriction placed on this keyring.
>> Basically I'm trying to follow the same design pattern.
>>
>> If requirements #1, #2 or both (#1 and #2) could be dropped, most of
>> this series would not be necessary.
>
> But without these requirements, the source of trust is unclear.
>
> Is there a reason why the MOK keyring is temporary?

I suppose it doesn't have to be temporary. I was trying not to introduce
another keyring within system_keyring code.

> Asumming a
> function similar to "restrict_link_by_builtin_and_secondary_trusted" is
> defined to include the MOK keyring, the CA keys in the MOK db would be
> loaded onto the MOK keyring, the other keys that meet the secondary
> keyring restriction would be loaded directly onto the secondary
> keyring[1], and as you currently have, the remaining keys onto the
> platform keyring.
>
> This eliminates the exemption needed for loading keys onto the
> secondary keyring. The MOK keyring, containing just CA keys, becomes a
> new trust source.

I just want to make sure I understand. If we kept the .mok keyring around,
we would store it into the system_keyring code, just like the platform
keyring is stored. This would allow the move exemption code to be removed.
If the mok keyring is a new trust source, whenever the secondary keyring
is referenced in verify_ code, the mok keyring will be checked too. If
I have this right, let me know and I’ll work on a v2. Thanks.