Re: [RFC PATCH v19 2/5] security: Add new SHOULD_EXEC_CHECK and SHOULD_EXEC_RESTRICT securebits

Mickaël Salaün
2024-07-09T20:42:48Z

On Mon, Jul 08, 2024 at 03:07:24PM -0700, Jeff Xu wrote:
> On Mon, Jul 8, 2024 at 2:25 PM Steve Dower <steve.dower@python.org> wrote:
> >
> > On 08/07/2024 22:15, Jeff Xu wrote:
> > > IIUC:
> > > CHECK=0, RESTRICT=0: do nothing, current behavior
> > > CHECK=1, RESTRICT=0: permissive mode - ignore AT_CHECK results.
> > > CHECK=0, RESTRICT=1: call AT_CHECK, deny if AT_CHECK failed, no exception.
> > > CHECK=1, RESTRICT=1: call AT_CHECK, deny if AT_CHECK failed, except
> > > those in the "checked-and-allowed" list.
> >
> > I had much the same question for Mickaël while working on this.
> >
> > Essentially, "CHECK=0, RESTRICT=1" means to restrict without checking.
> > In the context of a script or macro interpreter, this just means it will
> > never interpret any scripts. Non-binary code execution is fully disabled
> > in any part of the process that respects these bits.
> >
> I see, so Mickaël does mean this will block all scripts.

That is the initial idea.

> I guess, in the context of dynamic linker, this means: no more .so
> loading, even "dlopen" is called by an app ? But this will make the
> execve() fail.

Hmm, I'm not sure this "CHECK=0, RESTRICT=1" configuration would make
sense for a dynamic linker except maybe if we want to only allow static
binaries?

The CHECK and RESTRICT securebits are designed to make it possible a
"permissive mode" and an enforcement mode with the related locked
securebits. This is why this "CHECK=0, RESTRICT=1" combination looks a
bit weird. We can replace these securebits with others but I didn't
find a better (and simple) option. I don't think this is an issue
because with any security policy we can create unusable combinations.
The three other combinations makes a lot of sense though.

>
> > "CHECK=1, RESTRICT=1" means to restrict unless AT_CHECK passes. This
> > case is the allow list (or whatever mechanism is being used to determine
> > the result of an AT_CHECK check). The actual mechanism isn't the
> > business of the script interpreter at all, it just has to refuse to
> > execute anything that doesn't pass the check. So a generic interpreter
> > can implement a generic mechanism and leave the specifics to whoever
> > configures the machine.
> >
> In the context of dynamic linker. this means:
> if .so passed the AT_CHECK, ldopen() can still load it.
> If .so fails the AT_CHECK, ldopen() will fail too.

Correct

>
> Thanks
> -Jeff
>
> > The other two case are more obvious. "CHECK=0, RESTRICT=0" is the
> > zero-overhead case, while "CHECK=1, RESTRICT=0" might log, warn, or
> > otherwise audit the result of the check, but it won't restrict execution.
> >
> > Cheers,
> > Steve