Subject: Re: bug#54786: Installation tests are failing

From: [email-masked]
Date: Wed, 8 Jun 2022 02:58:09 +0200

Hi,

tl;dr: I hope there will be a security team discussing
       whether/how this kind of privileged execution interval
       could be exploited, and how to prevent such.
       
       E.g., could something that stealthily gets put in a finalizer
       for some innocent object be waiting to notice that it is running
       privileged, and do the next step in a dirty-work chain that
       sets things up nicely for e.g. remote DDOS control?

       Or is the independent FLOSS development process
       and its quality control being sabotaged stealthily
       with injections of "innocent mistakes" and
       (ultimately) trivial time-wasting bugs, making FLOSS projects
       look "not ready for production use" ?
       (despite the increasing evidence to the contrary)
              
       BTW, I think a minimalist/MES/RISCV team would be interesting!

       Regards,
       Bengt Richter
       
On +2022-06-07 16:00:54 +0200, Ludovic Courtès wrote:gets\
> Hi!
> 
> Maxim Cournoyer <[email-masked]> skribis:
> 
> > Ludovic Courtès <[email-masked]> writes:
> 
> [...]
> 
> >>> I reviewed how that works, and it'd be easy; I just didn't see the
> >>> incentive yet (there's no composition needed for the service, and it'd
> >>> make the definition slightly less readable).  If you tell me
> >>> mark+forkexec-constructor/container is going the way of the Dodo though,
> >>> that's a good enough incentive :-).
> >
> > That turns out to be bit problematic; dbus-daemon must not run in its
> > own user namespace (CLONE_NEWUSER) as it wants to validate user/group
> > IDs.  That's probably the reason it was working with
> > 'make-forkexec-constructor/container', as this was dropping the user and
> > net namespaces, contrary to least-authority, which uses them all.
> >
> > The problem then seems to be that since we need CAP_SYS_ADMIN when
> > dropping the user namespace, as CLONE_NEWUSER is what gives us
> > superpowers.  Per 'man user_namespaces':
> >
> >   The child process created by clone(2) with the CLONE_NEWUSER flag starts
> >   out with a complete set of capabilities in the new user namespace.
> >
> > Which means that if we combine something like (untested):
> >
> > (make-forkexec-constructor
> >   (least-authority
> >     (list (file-append coreutils "/bin/true"))
> >     (mappings (delq 'user %namespaces))
> >   #:user  "nobody"
> >   #:group "nobody"))
> >
> > the make-forkexec-constructor will switch to the non-privileged user
> > before the clone call is made, and it will fail with EPERM.
> >
> > When using 'make-forkexec-constructor/container', the clone(2) call
> > happens before switching user, thus as 'root' in Shepherd, which
> > explains why it works.
> 
> Damnit, that’s right.  For example the result of:
> 
>    (lower-object (least-authority-wrapper (file-append coreutils "/bin/uname")
>                                           #:namespaces (delq 'user %namespaces)))
> 
> won’t run as an unprivileged user:
> 
> --8<---------------cut here---------------start------------->8---
> $ $(guix build /gnu/store/hy8rd8p8pid67ac27dwm63svl5bqn0a1-pola-wrapper.drv)
> substitute: updating substitutes from 'https://ci.guix.gnu.org'... 100.0%
> substitute: updating substitutes from 'https://bordeaux.guix.gnu.org'... 100.0%
> substitute: updating substitutes from 'https://guix.bordeaux.inria.fr'... 100.0%
> The following derivations will be built:
>   /gnu/store/hy8rd8p8pid67ac27dwm63svl5bqn0a1-pola-wrapper.drv
>   /gnu/store/bd63i07rvvsw7xgsig0cbdsw7fpznd1k-references.drv
> building /gnu/store/bd63i07rvvsw7xgsig0cbdsw7fpznd1k-references.drv...
> successfully built /gnu/store/bd63i07rvvsw7xgsig0cbdsw7fpznd1k-references.drv
> building /gnu/store/hy8rd8p8pid67ac27dwm63svl5bqn0a1-pola-wrapper.drv...
> successfully built /gnu/store/hy8rd8p8pid67ac27dwm63svl5bqn0a1-pola-wrapper.drv
> Backtrace:
>            5 (primitive-load "/gnu/store/ifsh87aifh2k8pqzhkjxncq3vskpwx3l-pola-wrapper")
> In ice-9/eval.scm:
>    191:35  4 (_ #f)
> In gnu/build/linux-container.scm:
>     300:8  3 (call-with-temporary-directory #<procedure 7f9aa3a674b0 at gnu/build/linux-container.scm:396:3 (root)>)
>    397:16  2 (_ "/tmp/guix-directory.K9gBNH")
>     239:7  1 (run-container "/tmp/guix-directory.K9gBNH" (#<<file-system> device: "/gnu/store/jkjs0inmzhj4vsvclbf08nmh0shm7lrf-attr-2.5…> …) …)
> In guix/build/syscalls.scm:
>   1099:12  0 (_ 1845624849)
> 
> guix/build/syscalls.scm:1099:12: In procedure clone: 1845624849: Operation not permitted
> --8<---------------cut here---------------end--------------->8---
> 
> > I'm not sure how it could be fixed; it seems the user changing business
> > would need to be handled by the least-authority-wrapper code?  And the
> > make-forkexec-constructor would probably need to detect that command is
> > a pola wrapper and then avoid changing the user/group itself to not
> > interfere.
> 
> I think we would add #:user and #:group to ‘least-authority-wrapper’ and
> have it call setuid/setgid.  ‘make-forkexec-constructor’ doesn’t need to
> be modified, but the user simply won’t pass #:user and #:group to it.
> 
> Thanks,
> Ludo’.
> 
> 
>