Re: Inconsistent capability requirements for prctl_set_mm_exe_file()

From: Jann Horn
Date: Tue Oct 27 2020 - 08:52:04 EST


On Tue, Oct 27, 2020 at 1:11 PM Michael Kerrisk (man-pages)
<mtk.manpages@xxxxxxxxx> wrote:
> @Nicolas, your commit ebd6de6812387a changed the capability
> requirements for the prctl_set_mm_exe_file() operation from
>
> ns_capable(CAP_SYS_ADMIN)
>
> to
>
> ns_capable(CAP_SYS_ADMIN) || ns_capable(CAP_CHECKPOINT_RESTORE).
>
> That's fine I guess, but while looking at that change, I found
> an anomaly.
>
> The same prctl_set_mm_exe_file() functionality is also available
> via the prctl() PR_SET_MM_EXE_FILE operation, which was added
> by Cyrill's commit b32dfe377102ce668. However, there the
> prctl_set_mm_exe_file() operation is guarded by a check
>
> capable(CAP_SYS_RESOURCE).
>
> There are two things I note:
>
> * The capability requirements are different in the two cases.
> * In one case the checks are with ns_capable(), while in the
> other case the check is with capable().
>
> In both cases, the inconsistencies predate Nicolas's patch,
> and appear to have been introduced in Kirill Tkhai's commit
> 4d28df6152aa3ff.
>
> I'm not sure what is right, but those inconsistencies seem
> seem odd, and presumably unintended. Similarly, I'm not
> sure what fix, if any, should be applied. However, I thought
> it worth mentioning these details, since the situation is odd
> and surprising.

FWIW, as a bit of context here: I believe that these checks are more
driven by "what capabilitiies do we think a typical caller will have"
than by a proper security design of "what capabilities do we have to
require to establish certain security guarantees". As people have
noted elsewhere, on a system without LSMs, a process can point
/proc/self/exe to almost any executable file of its choice anyway (by
executing that file and then replacing the executable code of the
resulting process).

The properly engineered solution would probably be to let LSMs hook
these APIs (if they care) and then remove the capable()/ns_capable()
checks.