-
Notifications
You must be signed in to change notification settings - Fork 771
Description
(Note: This is long, and meandering, and wasn't even typed quite in the order I finally put it together in, and as so is even more overly confusing than this insanely long run-on sentence that I've stuck at the beginning of this issue so as to provide a warning that the content is largely incomprehensible... frowny.)
When you log into a system, you do so with a username, which is stored in utmp. Programs that care about the "logged in user"--which might be unrelated to the user that owns the currently executing process--can then look up that username using getlogin(), which goes back into utmp to look up the data.
The reason why programs that want to know who is logged in use getlogin, a function that returns a username (as opposed to a function that returns a uid and then getpwnam), is that the username->uid->username mapping might not successfully round-trip on any given system.
On my servers, I have multiple login aliases to the same account. This allows me to provide multiple people root access, and yet have them all maintain separate passwords, separate home directories (so as to use personal settings for .vimrc, not for storing private data), separate hardware two-factor authentication token seeds, and separate screen sessions (which screen organizes by way of the logged-in username).
Meanwhile, on some devices, you simply can't perform the uid->username lookup reliably because the information is entirely not present on your local system (due to usage of an external or remote authentication system) or is simply not accessible to your user account.
(This situation actually comes up commonly on Android devices, as the username mapping table is stored as a hardcoded array in various places in bionic. If you have processes sitting in different chroots, especially if they are using glibc, you will get failures attempting to do reverse lookups.)
Put differently, the core problem is simply that the uid->username mapping is non-normative: users do not log in with uids, they log in with usernames. The mechanisms for handling the reverse lookup have no guarantee to accurately reflect the result of the forward lookup. If you want to replicate the process later, you have to start from the original username.
Given this, the fundamental mistake that libutempter is then making is that the API doesn't even seem to allow the daemon to pass the original username used to look up the account: instead, it insists (I currently assume for a security reason, due to its use case) upon attempting to auto-detect the username using getpwuid(geteuid()) in a separately-executed helper process.
This result, though, simply doesn't work if you are dealing with any of the above situations. In my case, if I log in with mosh, I have access to none of my screen sessions (which actually makes mosh useless to me ;P), as utmp has the wrong username stored in it (which causes screen to look in someone else's folder to find my active sessions).
So, on the one hand, this seems like "bug in libutempter", but it isn't really the implementation that is the problem: it is in fact the API of libutempter itself that contains a design flaw. Given its API, I'm not even certain if it is really a bug: it seems like utempter may in fact supposed to only be used as a fallback for applications that don't have root access.
In fact, that does seem to be why the library exists, and this seems to be exactly what mosh is: an unpriviledged process that seems to want to edit utmp (seemingly to unregister and reregister the user attached to the session so they aren't considered logged in while disconnected): it needs to go through this helper API (and it can't just let sshd manage it as sshd insists on deleting the entry when the original session disconnects).
Looking at it from this level, we can compare the behavior to tmux and screen. The behavior of tmux is ludicrous: it allocates peudo-terminals without adding them to utmp, so if you call getlogin() you get back NULL, causing programs that rely on that (such as screen, humorously) to have to fall back to incorrectly doing uid->username reverse mappings.
What screen does is more interesting: it has a bunch of code for using libutempter, but will only use it if it needs to: if it has enough privileges to directly access utmp it avoids relying on the setuid wrappers, seemingly because of limitations with the API (there are multiple comments in the code bemoaning "login slots" not working correctly).
Instead, screen seems to simply be setgid utmp, which allows it to have access to the utmp database. Is there a reason I'm not understanding as to why mosh can't also be setgid utmp, and can't itself also modify the utmp database like screen does? I believe that this can be used to fix the login name issue (as the login name can be copied from the original pty through to the login shell).