core icon indicating copy to clipboard operation
core copied to clipboard

Allow child processes to setgroups() on SmartOS LX

Open smokris opened this issue 3 years ago • 4 comments

I'm trying to run Dovecot 2.3.19 (RPM from repo.dovecot.org) in CentOS Stream 8, running in a SmartOS LX zone (an Illumos kernel with a Linux syscall emulation layer).

When I launch Dovecot, it emits this message:

log(…): Fatal: setgroups() failed: Operation not permitted

If I understand correctly, the following sequence of events leads to that error:

  1. master/main.c calls drop_capabilities(), which updates the master process to have only certain Permitted and Effective capabilities, and no Inheritable capabilities
  2. master/main.c forks and execs the log process
  3. log/main.c calls restrict_access_by_env(), which calls restrict_access(), which calls fix_groups_list(), which calls setgroups()
  4. setgroups() fails because the log process does not have the CAP_SETGID capability (or any capabilities, for that matter), because in step 1 the master process specified no Inheritable capabilities

Therefore I think the solution is to have drop_capabilities() also specify Inheritable capabilities.

Test

dovecot-2.3.19-2.x86_64.rpm from repo.dovecot.org

# uname -a
Linux localhost 3.13.0 BrandZ virtual linux x86_64 x86_64 x86_64 GNU/Linux
# cat /etc/system-release
CentOS Stream release 8
# /usr/sbin/dovecot -F
May 12 19:42:03 master: Info: Dovecot v2.3.19 (b3ad6004dc) starting up for imap, pop3, lmtp (core dumps disabled)
May 12 19:42:03 log(96325): Fatal: setgroups() failed: Operation not permitted
May 12 19:42:03 master: Error: service(log): child 96325 returned error 89 (Fatal failure)
May 12 19:42:03 master: Error: service(log): command startup failed, throttling for 2.000 secs
May 12 19:42:03 master: Error: service(config): command startup failed, throttling for 2.000 secs
May 12 19:42:03 master: Error: service(anvil): command startup failed, throttling for 2.000 secs
May 12 19:42:08 master: Error: service(config): command startup failed, throttling for 4.000 secs
May 12 19:42:08 master: Error: service(stats): command startup failed, throttling for 2.000 secs
May 12 19:42:12 master: Error: service(config): command startup failed, throttling for 8.000 secs
May 12 19:42:12 master: Error: service(imap-login): command startup failed, throttling for 2.000 secs

# # from another terminal:
# nc 10.0.42.6 143 < /dev/null
# # (no output)

✅ Local build with this MR

# /opt/dovecot/sbin/dovecot -F -c /etc/dovecot/dovecot.conf
May 12 19:43:08 master: Info: Dovecot v0.0.0-30709+7e36988a4f-dirty (7e36988a4f) starting up for imap, pop3, lmtp (core dumps disabled)
May 12 19:43:16 imap-login: Info: Disconnected: Connection closed (no auth attempts in 0 secs): user=<>, rip=10.0.43.2, lip=10.0.42.6, session=<qsAvH9neG9MKACsC>

# # from another terminal:
# nc 10.0.42.6 143 < /dev/null
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ STARTTLS LOGINDISABLED] Dovecot ready.

smokris avatar May 12 '22 23:05 smokris

Hmm. I don't completely understand Linux capabilities, and the current capabilities code in Dovecot was written by other people. But my understanding is that Dovecot uses them to drop privileges that root generally has. I don't think Dovecot supports starting itself as non-root but with the necessary capabilities, which is what capabilities are also used for.

So I'm a bit worried about changing security sensitive code that I don't fully understand. Since it works in normal Linux, isn't this an incompatibility bug in SmartOS LX instead? It seems like if I add extra capabilities it's possible that there might now be too many capabilities. Like would it allow setgid() calls even after dropping root privileges?

sirainen avatar May 16 '22 09:05 sirainen

This changes the capabilities to be inheritable, which in SetCap would look like CapName+ip instead of CapName+p. It does not actually change what capabilities are provided.

cmouse avatar May 16 '22 10:05 cmouse

Hi, @sirainen and @cmouse, and thanks for the feedback.

[sirainen] Since it works in normal Linux, isn't this an incompatibility bug in SmartOS LX instead?

Hmm, from my understanding of how the capabilities system works, the SmartOS LX behavior seems POSIX-compliant(*) to me.

(*) Er, "compliant with the withdrawn POSIX 1003.1e spec", I mean.)

I'm not sure why the existing Dovecot code works on Linux. The Linux Kernel's setgroups() implementation checks whether the current namespace has CAP_SETGID — so the child process needs to have that capability. But AFAIK the child process can only receive it by inheriting from a process that has that capability in both its Permitted and Inheritable sets. Since the existing Dovecot code specifies an empty Inheritable set, I would expect the child process to not have CAP_SETGID, and thus I would expect its setgroups() call to fail.

[cmouse] This changes the capabilities to be inheritable, which in SetCap would look like CapName+ip instead of CapName+p. It does not actually change what capabilities are provided.

Yes, that's my intent — this PR grants the master process's existing capabilities to its child processes, so the child processes have permission to drop root privileges.

smokris avatar May 16 '22 18:05 smokris

would it allow setgid() calls even after dropping root privileges?

Ah, good point. I wrote a quick test — https://github.com/smokris/drop-priv-test — and on both a real Linux kernel and SmartOS LX, setgid() and setuid() are blocked after dropping root privileges, even if the capabilities are inheritable:

# uname -a
Linux localhost 4.18.0-383.el8.x86_64 #1 SMP Wed Apr 20 15:38:08 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
# make && ./test-parent
cc -lcap -o test-parent test-parent.c
cc -lcap -o test-child test-child.c
test-parent: ========= no cap_set_proc
 test-child: uid=0 euid=0 gid=0 egid=0
 test-child: dropping root privileges...
 test-child: uid=89 euid=89 gid=89 egid=89
 test-child: attempting to regain root privileges...
 test-child:     setgid failed: Operation not permitted
 test-child:     setuid failed: Operation not permitted
 test-child: uid=89 euid=89 gid=89 egid=89
test-parent: ========= cap_set_proc with Permitted and Effective
 test-child: uid=0 euid=0 gid=0 egid=0
 test-child: dropping root privileges...
 test-child: uid=89 euid=89 gid=89 egid=89
 test-child: attempting to regain root privileges...
 test-child:     setgid failed: Operation not permitted
 test-child:     setuid failed: Operation not permitted
 test-child: uid=89 euid=89 gid=89 egid=89
test-parent: ========= cap_set_proc with Permitted and Effective and Inheritable
 test-child: uid=0 euid=0 gid=0 egid=0
 test-child: dropping root privileges...
 test-child: uid=89 euid=89 gid=89 egid=89
 test-child: attempting to regain root privileges...
 test-child:     setgid failed: Operation not permitted
 test-child:     setuid failed: Operation not permitted
 test-child: uid=89 euid=89 gid=89 egid=89

smokris avatar May 17 '22 00:05 smokris

I need root too

xdkaka avatar Nov 26 '22 05:11 xdkaka

merged as 88a558776a10c7b3bef9fcb60126418cda3681e2

cmouse avatar Sep 25 '23 12:09 cmouse