Re: [PATCH 0/3] fs: reduce export usage of kerne_read*() calls

From: Mimi Zohar
Date: Sat May 23 2020 - 22:54:09 EST


On Fri, 2020-05-22 at 16:25 -0700, Scott Branden wrote:
> Hi Kees,
>
> On 2020-05-22 4:04 p.m., Kees Cook wrote:
> > On Fri, May 22, 2020 at 03:24:32PM -0700, Scott Branden wrote:
> >> On 2020-05-18 5:37 a.m., Mimi Zohar wrote:
> >>> On Sun, 2020-05-17 at 23:22 -0700, Christoph Hellwig wrote:
> >>>> On Fri, May 15, 2020 at 09:29:33PM +0000, Luis Chamberlain wrote:
> >>>>> On Wed, May 13, 2020 at 11:17:36AM -0700, Christoph Hellwig wrote:
> >>>>>> Can you also move kernel_read_* out of fs.h? That header gets pulled
> >>>>>> in just about everywhere and doesn't really need function not related
> >>>>>> to the general fs interface.
> >>>>> Sure, where should I dump these?
> >>>> Maybe a new linux/kernel_read_file.h? Bonus points for a small top
> >>>> of the file comment explaining the point of the interface, which I
> >>>> still don't get :)
> >>> Instead of rolling your own method of having the kernel read a file,
> >>> which requires call specific security hooks, this interface provides a
> >>> single generic set of pre and post security hooks.ÂÂThe
> >>> kernel_read_file_id enumeration permits the security hook to
> >>> differentiate between callers.
> >>>
> >>> To comply with secure and trusted boot concepts, a file cannot be
> >>> accessible to the caller until after it has been measured and/or the
> >>> integrity (hash/signature) appraised.
> >>>
> >>> In some cases, the file was previously read twice, first to measure
> >>> and/or appraise the file and then read again into a buffer for
> >>> use.ÂÂThis interface reads the file into a buffer once, calls the
> >>> generic post security hook, before providing the buffer to the caller.
> >>> Â(Note using firmware pre-allocated memory might be an issue.)
> >>>
> >>> Partial reading firmware will result in needing to pre-read the entire
> >>> file, most likely on the security pre hook.
> >> The entire file may be very large and not fit into a buffer.
> >> Hence one of the reasons for a partial read of the file.
> >> For security purposes, you need to change your code to limit the amount
> >> of data it reads into a buffer at one time to not consume or run out of much
> >> memory.
> > Hm? That's not how whole-file hashing works. :)
>
> >
> > These hooks need to finish their hashing and policy checking before they
> > can allow the rest of the code to move forward. (That's why it's a
> > security hook.) If kernel memory utilization is the primary concern,
> > then sure, things could be rearranged to do partial read and update the
> > hash incrementally, but the entire file still needs to be locked,
> > entirely hashed by hook, then read by the caller, then unlocked and
> > released.

Exactly.

> >
> > So, if you want to have partial file reads work, you'll need to
> > rearchitect the way this works to avoid regressing the security coverage
> > of these operations.
> I am not familiar with how the security handling code works at all.
> Is the same security check run on files opened from user space?
> A file could be huge.
>
> If it assumes there is there is enough memory available to read the
> entire file into kernel space then the improvement below can be left as
> a memory optimization to be done in an independent (or future) patch series.

There are two security hooks - security_kernel_read_file(),
security_kernel_post_read_file - in kernel_read_file(). ÂThe first
hook is called before the file is read into a buffer, while the second
hook is called afterwards.

For partial reads, measuring the firmware and verifying the firmware's
signature will need to be done on the security_kernel_read_file()
hook.

>
> > So, probably, the code will look something like:
> >
> >
> > file = kernel_open_file_for_reading(...)
> > file = open...
> > disallow_writes(file);
> > while (processed < size-of-file) {
> > buf = read(file, size...)
> > security_file_read_partial(buf)
> > }
> > ret = security_file_read_finished(file);
> > if (ret < 0) {
> > allow_writes(file);
> > return PTR_ERR(ret);
> > }
> > return file;
> >
> > while (processed < size-of-file) {
> > buf = read(file, size...)
> > firmware_send_partial(buf);
> > }
> >
> > kernel_close_file_for_reading(file)
> > allow_writes(file);

Right, the ima_file_mmap(), ima_bprm_check(), and ima_file_check()
hooks call process_measurement() to do this. Âima_post_read_file()
passes a buffer to process_measurement() instead.

Scott, the change should be straight forward. ÂThe additional patch
needs to:
- define a new kernel_read_file_id enumeration, like
FIRMWARE_PARTIAL_READ.
- Currently ima_read_file() has a comment about pre-allocated firmware
buffers. ÂUpdate ima_read_file() to call process_measurement() for the
new enumeration FIRMWARE_PARTIAL_READ and update ima_post_read_file()
to return immediately.

The built-in IMA measurement policy contains a rule to measure
firmware. ÂThe policy can be specified on the boot command line by
specifying "ima_policy=tcb". ÂAfter reading the firmware, the firmware
measurement should be in <securityfs>/ima/ascii_runtime_measurements.

thanks,

Mimi