Thread (80 messages) 80 messages, 12 authors, 2024-08-09

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

From: Jeff Xu <hidden>
Date: 2024-07-09 21:58:24
Also in: linux-fsdevel, linux-integrity, linux-security-module, lkml

On Tue, Jul 9, 2024 at 1:42 PM Mickaël Salaün [off-list ref] wrote:
On Mon, Jul 08, 2024 at 03:07:24PM -0700, Jeff Xu wrote:
quoted
On Mon, Jul 8, 2024 at 2:25 PM Steve Dower [off-list ref] wrote:
quoted
On 08/07/2024 22:15, Jeff Xu wrote:
quoted
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.
quoted
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.
If we need only handle 3  combinations,  I would think something like
below is easier to understand, and don't have wield state like
CHECK=0, RESTRICT=1

XX_RESTRICT: when true: Perform the AT_CHECK, and deny the executable
after AT_CHECK fails.
XX_RESTRICT_PERMISSIVE:  take effect when XX_RESTRICT is true. True
means Ignoring the AT_CHECK result.

Or

XX_CHECK: when true: Perform the AT_CHECK.
XX_CHECK_ENFORCE takes effect only when XX_CHECK is true.   True means
restrict the executable when AT_CHECK failed; false means ignore the
AT_CHECK failure.

Of course, we can replace XX_CHECK_ENFORCE with XX_RESTRICT.
Personally I think having _CHECK_ in the name implies the XX_CHECK
needs to be true as a prerequisite for this flag , but that is my
opinion only. As long as the semantics are clear as part of the
comments of definition in code,  it is fine.

Thanks
-Jeff

quoted
quoted
"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
quoted
Thanks
-Jeff
quoted
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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help