-
Notifications
You must be signed in to change notification settings - Fork 28
NSS: avoid self connections #1013
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
We were running the NSS integration test without authd actually using the library, do it now to simulate a more real scenario Even though it is gonna be a no-op soon
ab1e3c2
to
f49a9f6
Compare
…itself When authd does any NSS request it ends up calling itself again, and while this is not a problem per se it is just not required because we always assume in authd code that our users are not part of the local entries, when we ask for, given that we are in control of the database. It's also potentially causing dead-locks, since we may end up calling NSS functions when we have a lock, and these may lead to calling authd again. As per this, avoid performing this when the NSS module is loaded by authd process itself. To avoid this we expose the authd PID as an environment variable that also the NSS module can read, even before connecting, and we check that the current module PID matches with the variable. Now, this is still not enough to be sure though, since any other process may just replicate the same by defining the env variable and thus bypassing the check. So, to prevent this to happen, in case all the previous checks have passed, get the peer credentials from the unix stream, and verify that the socket is actually owned by the PID advertised by the env variable. And only in this case, ignore invoking authd for all the NSS requests.
Since for each process NSS will be loaded just once there's no need to perform the authd-self-check on all the requests, but we can just do it the first time and then rely on such result. So use an OnceLock to do it
The PID of the library won't change during the execution, so perform the test and save the information for later use, instead of repeating the same check for each request
f49a9f6
to
21a1ce8
Compare
Do I understand correctly that the potential threat we want to protect against is that an attacker who can control the environment of a program is able to make the program skip NSS requests to authd users/groups? If so, I don't think checking the peer creds and comparing the PID from that with the PID from the env var is enough. An attacker could just guess the PID of the process and set the env var accordingly. If they repeat it often enough, they will guess correctly eventually (the PID space is too small to avoid that). Checking the name of the currently running executable would be more secure. |
Considering that an attacker who can control the environment can also set
So I think the correct check we should do before skipping self lookups is |
I'll prepare a commit for that. |
Another thing to consider: Do we really want to avoid self-lookups in all the NSS requests done by authd? There might be cases where we are not checking our own database first, and instead rely on NSS to do that for us. For example when checking if a generated UID is unique:
AFAICT we don't check our own database first in that case, but rely on NSS to do that. That case might be moot once #993 is merged, but there could be others. That makes me a bit reluctant of the approach to always skip self-lookups in our NSS module. I would prefer it if we could define functions in Go which we can call to do NSS lookups without self-lookups, but can also still do NSS lookups with self-lookups. |
We might be able to achieve that by using thread-local variables instead of environment variables. Implementing that seems like too much effort for something that we didn't plan, so I'll not do that for now. Avoiding self-requests is a nice performance optimization, but it's not high priority IMO, so I think we should revisit this only once we don't have any higher priority issues to work on. |
This PR is meant to be based on what we are doing on #993 and so there we don't rely on it, reason why I think we should just never check for it and always assume it. Use per-request variables IMHO it can just lead to racy situations, and I think it's better (since it's also easier to test for us) if we assume that a NSS request done by the daemon won't ever return a result, or we end up in inconsistent situations that are quite hard to test too (they can, but only in integration tests). While we have already lots of tests in |
I don't see that, how can it be racy if the variables are bound to a single thread and a single request?
why are they hard to test? we could for example implement my proposal by changing the signature of Even if we're not doing any lookups which rely on NSS to check our own database with #993 (I did not check if that's the case or not), I think it's better to make it explicit in the function call whether internal entries are considered or not. We (or someone else working on the code base) might later find it unexpected when e.g. |
When authd does any NSS request it ends up calling itself again, and
while this is not a problem per se it is just not required because we
always assume in authd code that our users are not part of the local
entries, when we ask for, given that we are in control of the database.
It's also potentially causing dead-locks, since we may end up calling
NSS functions when we have a lock, and these may lead to calling authd
again.
As per this, avoid performing this when the NSS module is loaded by
authd process itself.
To avoid this we expose the authd PID as an environment variable that
also the NSS module can read, even before connecting, and we check that
the current module PID matches with the variable.
Now, this is still not enough to be sure though, since any other process
may just replicate the same by defining the env variable and thus
bypassing the check.
So, to prevent this to happen, in case all the previous checks have
passed, get the peer credentials from the unix stream, and verify that
the socket is actually owned by the PID advertised by the env variable.
And only in this case, ignore invoking authd for all the NSS requests.
UDENG-7313