Repository navigation
secure_getenv(3) makes it difficult for clients to use gss-proxy with SELinux and domain transitions due to AT_SECURE flag #127
Description
Activity
What sets the AT_SECURE flag for the unprivileged child?
Perhaps the dovecot code should ensure it is cleared at execve() ?
After all the child environment is fully controlled by the parent (and you will have to figure out if the parent sets/retains environment variables like GSS_USE_PROXY for the child), so it should clear AT_SECURE?That said we could consider making the plugin not use secure_getenv() but the tricky part is figuring out if it is safe to do so, he env var won't change what code is loaded, so that is not a problem, but it will make setuid processes suddenly interposed, so it is a change in behavior that could break other existing users.
Perhaps we could add a separate mechanism that does not depend on environment variables. Potentially we could source a config file, or check for the presence of a file in some specific directory in the user's home directory. The problem is that we can't trust environment variables to figure where the toggle is.
If you have good ideas I am all ears.
What sets the AT_SECURE flag for the unprivileged child? Perhaps the dovecot code should ensure it is cleared at execve() ?
The flag is being set by SELinux when a process in one domain executes a file with a label that triggers a transition into a different domain. It prevents the parent using
LD_PRELOAD, etc., to be able to execute arbitrary code after the child process transitions to the new domain.(If you want the full details, see https://unix.stackexchange.com/questions/806436/can-gssproxy-be-used-with-dovecot-together-under-selinux - I only had a vague idea about all of this before going through that debugging process!)
Anyway, it's not something the parent process is doing nor can the parent process prevent it from happening (by design). As the sysadmin I can modify the policy to not set the bit on this particular transition from
dovecot_ttodovecot_exec_tbut I'm reluctant to do that because the LSM is setting AT_SECURE for a good reason. :)That said we could consider making the plugin not use secure_getenv() but the tricky part is figuring out if it is safe to do so
So it comes down to what your reasoning is for preventing the plug-in from activating for setuid executables. If you need to duplicate the exact same logic that the kernel has for signalling to user processes that their security context has changed & not to trust their environment then one way or another you need to check the AT_SECURE flag. But here's a proposal to which allows the gss-proxy configuration to determine whether the client should respect AT_SECURE or not:
- Add a new setting in the gss-proxy service definition named something like
noatsecure. - The plug-in calls getauxv(3) to find out if the hosting process has
AT_SECUREset. - The plug-in uses getenv(3) to obtain
USE_GSS_PROXY=andGSSPROXY_SOCKET=unconditionally. - If
AT_SECUREis not set, the plug-in uses the socket as normal. - If
AT_SECUREis set, the plug-in connects to the socket, checks SO_PEERCRED to verify that it's connected to the genuine root-owned gss-proxy service, and then sends a request to ask "what is the value ofnoatsecurefor my service?" - gss-proxy matches the request to a service definition and replies appropriately
- The plug-in activates or not depending on the answer.
You could even move the policy decision of whether to activate or not into gss-proxy entirely: it could open
/proc/<pid>/auxvand look for the value of AT_SECURE therein. Although this wouldn't be race-free, the server is already doing something similar with/proc/<pid>exeto perform process name matching.- Add a new setting in the gss-proxy service definition named something like
Sounds like a SeLinux policy that should be changed then.
In this case the parent dovecot process is not running some external root setuid process that could lead to privilege escalation, it is in fact lowering privileges and is in full control of what it decides to run.
This should be reported upstream as well and not just changed locally.
If the proposal requires the plugin to connect to the gss-proxy regardless, then we do not need to do any dance detecting AT_SECURE, we could arbitrarily do so if the getenv fails.
We would not be able to use GSSPROXY_SOCKET, so only connecting to the system defined socket would b e possible and this implicitly assures this is a sanctioned gss-proxy because only the root (or whatever user the admin decided to run gss-proxy as) can open a listening socket in the default directory.
The question is, how does gss-proxy know if it should service the specific process ?
Should we add an allow-list that only applies to clients asking if they should interpose ?
Should we deprecate GSS_USE_PROXY and use this mechanism always ?Uhmmm another option is to offer an additional socket for processes that have this issue, and have them connect only to the .._atsecure socket ... then we do not need a special call.
Gss-proxy configuration would turn on that socket only for specific services and if the permission don't match then forwarding requests will be given errors and the interposer falls back to local handling.
The only annoyance about this is that now any AT_SECURE process that may be using gssapi now suddenly always starts trying this roundtrip. But perhaps a selinux boolean/policy can be used to determine if those process should be even able to connect to the _atsecure socket, and cut the roundtrip short if that is not desired only for some processes ?
even better the socket could be named after the process name, that would automatically restrict the service to specific clients, and would only cost a failed open on a non-existing socket for all other cases ...
I did some comparisons between what
dovecot_auth_tcan do extra todovecot_tto see what the effect of modifying the policy might be. There are a few things in there that it would be best to not allowdovecot_tto do if possible, such as accessing files labelled withdovecot_passwd_t, using D-Bus, using to systemd-logind, talking to rpcbind and so on. I was going to update the Stack Exchange question once I've gone through them all.But anyway... if the noatsecure socket had a separate label like
gssproxy_noatsecure_sock_tthen the local admin could create a one-liner policy to grant a specific domain likedovecot_auth_tpermission to use the socket. If not then SELinux will deny access. This will cause a log message each time it happens, but that can be silenced by default with a dontaudit rule (although generally these hidden denials are annoying to debug!).A boolean could be created but it would end up being rather broad, presumably it would be something like "allow daemons to use the noatsecure socket" any apply to lots of domains. Though maybe that would be fine? It all comes down to the exact reason that AT_SECURE is respected in the first place: I'm still not sure whether it's a security check or just a policy one, to avoid unexpected behaviour...
I am leaning towards adding sockets named after the argv[0] of the process that should be authorized to use it.
This will implicitly self-select out most processes with AT_SECURE set, unles they are explicitly added in configuration.
Normal service configuration (including checking peer selinux label) can do the rest from the gss-proxy side.Can you easily tell what is the process name of the dovecot helper process?
Thank you! It will take me a little while to test it out.
The Dovecot authentication server is:
PID EXE COMMAND S TTY TIME COMMAND 408705 /usr/libexec/dovecot/auth auth S ? 00:00:00 /usr/libexec/dovecot/authso I guess it will use a socket called
/var/lib/gssproxy/default.sock.auth.authis an annoyingly generic basename, but in the real world the potential for clashes between differentAT_SECUREprograms both using gss-proxy on the same machine is low.Is the name hard coded in dovecot, or is it something that could be easily renamed to dovecot_auth should there be the need?
Unfortunately the amount of info I can gather from inside the plugin is limited and the program name seemed the most distinguishing/predictable for for a socket name.Feel free to take your time, I am i no hurry to merge this, sadly it is not the easiest to test within the testing framework, but I'll see if I can add a github workflow that can do it.
If there was a clash I would try to resolve it by creating a hardlink by a different name & configuring Dovecot to launch the hardlink instead. So I think using the basename of the process's executable is OK.
@yrro any luck testing this ?
I have tested this with Postfix on Fedora 44 and I'm happy to report that it works! Thank you!
Here's the gssproxy config:
[service/postfix] mechs = krb5 cred_store = client_keytab:/etc/gssproxy/postfix.keytab cred_usage = initiate euid = postfix socket = /var/lib/gssproxy/default.sock.smtp program = /usr/libexec/postfix/smtpOn the Postfix side I had to configure:
import_environment = MAIL_CONFIG MAIL_DEBUG MAIL_LOGTAG TZ XAUTHORITY DISPLAY LANG=C POSTLOG_SERVICE POSTLOG_HOSTNAME GSS_USE_PROXY=yes... plus putting
postfix_smtp_tinto permissive mode so that it's allowed to connect to the gssproxy sockets.BTW, Postfix is another example of a program with a few different executables each of which have a relatively short name that could clash with other programs. For instance, both Postfix and Dovecot have an
lmtpexecutable... it would not be totally outside of the realms of possibility to want to configure Postfix's lmtp client to authenticate using GSSAPI to Dovecot's lmtp server. Nonetheless this is probably pretty unusual in practice and certainly for my use cases (Postfix smtp initiating & Dovecot auth responding) it will work just fine.Yeah name clashes can happen but we have additional controls (program name, selinux context, permissions) on the server side that can restrict which clients are permitted to connect anyway.
Oh I'm with you I think.... gssproxy can have two services defined with the same socket name, and use the euid/program name to route requests to the correct service.
I was not able to get Dovecot to be able to use gss-proxy.
The problem is that the master dovecot process, running as
rootwithindovecot_truns the Dovecot authentication server as a separate process as thedovecotuser withindovecot_auth_t. When the process transition occurs, the child process runs with theAT_SECUREflag set.In this situation, secure_getenv(3) always returns 0, therefore the plugin never activates because it is not able to see the value of the
GSS_USE_PROXY=environment variable.