Unix shells are generally not viable access control mechanisms any more

September 17, 2023

Once upon a time, if you had a collection of Unix systems, you could reasonably do a certain amount of access control to your overall environment by forcing logins to have specific administrative shells. As a bonus, these administrative shells could print helpful messages about why the particular login wasn't being allowed to use your system. This is a quite attractive bundle of features, but unfortunately this no longer works in a (modern) Unix environment with logins (such as we have). There are two core problems.

First, you almost certainly operate a variety of services that normally only use Unix logins as a source of (password) authentication and perhaps a UID to operate as, and ignore the login's shell. This is the common pattern of Samba, IMAP servers, Apache HTTP Basic Authentication, and so on. In some cases you may be able to teach these services to look at the login's shell and do special things, but some of them are sealed black boxes and even the ones that can be changed require you to go out of your way. If you forget one, it fails open (allowing access to people with an administrative shell that should lock them out).

(One of these services is SSH itself, since you can generally initiate SSH sessions and ask for port forwarding or other features that don't cause SSH to run the login shell.)

Second, you may operate general authentication services, such as LDAP or a Single Sign On system, and if you do these authentication services are generally blind to what they're being used for and thus to whether or not a login with a special shell should be allowed to pass this particular authentication. The only real solution is to have multiple versions of these authentication systems with different logins in them, and point systems at different ones based on exactly who should be allowed to use them.

A similar issue happens with Apache HTTP Basic Authentication in common configurations, where you have a single authentication realm with a single Apache htpasswd file that covers an assortment of different services. If you need certain logins ('locked' logins or the like) to be excluded from some of these services but not others, either you need multiple htpasswd files (at least) or you need to teach each such service to do additional checks.

(In general you're going to have to try to carefully review who should be able to use which of your services when, and the resulting matrix is often surprisingly complicated and tangled. Life gets more complicated if you're using administrative shells for reasons other than just locking people out with a message, for example to try to force an initial password change.)

Today, the only two measures of login access control that really work in a general environment are either scrambling the login's password (and disable any SSH authorized keys) or excluding the login entirely from your various authentication data sources (your LDAP servers, your Apache htpasswd files, and so on). It's a pity that changing people's shells is no longer enough (it was both easy and convenient), but that's how the environment has evolved.

Comments on this page:

From at 2023-09-18 04:35:49:

You still have a "close enough" equivalent – if your accounts are stored in /etc/passwd, then hopefully all services using them do so through PAM and not through direct file access. So in that case you'll still have PAM honor the "lockout" via chage -l (or wherever it was) due to account/pam_unix enforcing the check, and you should still have it honor an invalid shell due to pam_shells.so enforcing that.

(And if the services directly access /etc/shadow, Slackware style, I would honestly say that's a self-inflicted problem...)

From at 2023-09-18 09:20:53:

And additionally if you have PAM you can use /etc/security/access.conf to limit login to a specific user group or lock out other users/groups.

By cks at 2023-09-18 14:24:39:

Unfortunately, a variety of common authentication systems don't use PAM. Apache htpasswd definitely doesn't, and I believe that Samba doesn't normally (it has its own separate database, which is normally synchronized to your Unix passwords). I'm not sure if common LDAP servers use PAM when you authenticate through them, but there you have the problem that often what matters is the specific LDAP client the user is authenticating to, not the LDAP server. And various things that authenticate via LDAP aren't even thinking about Unix users, so they definitely don't use PAM; they just consider LDAP an authoritative source of users, maybe user attributes, and authentication.

Apache trying to interpret the shell field seems like a huge conflation of concerns and the wrong kind of magic to me. Even login(1) doesn’t actually attempt that: all it does it run the specified executable. It is the executable which then implements some policy – not login(1). Of course this is not an option for Apache; I don’t think it’s feasible for it to somehow defer to the specified executable in the same way. “Then it should assign meaning based on the path name itself” seems entirely the wrong reaction, though.

What is the requirement lurking in the background here?

A UID is good for two things in Unix: owning files and owning processes.

Is the idea that you want an easy way to say “this UID should never be allowed to own a process”? That seems a reasonable desire, just the shell passwd field not the right layer to do it with.

Is the idea that the UID may own processes but you want to be able to restrict it to fixed set of what kind of process? A more complicated want, but again: reasonable but not for passwd.

Or is the idea that the UID may own arbitrary processes, but you just don’t want it to be able to log in? In that case I’m not sure I can picture why you’d want that. What makes logging in special? A shell is just a process, and can be used to create other processes; if it’s OK for the UID to own other processes that may do whatever, what makes shell especially undesirable? What even constitutes “logging in” really?

(As a sidebar, running through all the cases, but for the other thing a UID is good for: being able to limit a UID to owning processes but not files seems an interesting idea that might possibly be useful but I can’t actually imagine how. More obviously useful would be to be able to restrict file ownership to a certain set of files; on one hand, d’uh, this already exists and is called permissions; OTOH in the existing mechanism the file is the object on which access control is specified, and it might be potentially be useful and allow for better clarity to be able to specify policy based on the UID (“in the FS root only these accounts have access to create files” (with implicit MAC based on what is then created there) vs “this UID is prohibited from owning files anywhere but this directory” (which would apply regardless what sort of things anyone puts anywhere else in the FS)). For the login special case restriction, I can’t even think of what the file ownership parallel could be.)

From at 2023-09-19 23:58:47:

and I believe that Samba doesn't normally (it has its own separate database, which is normally synchronized to your Unix passwords).

It still has the ability to call PAM for authorization, though (just like e.g. sshd still does call PAM for the authorization functions even if you log in via public-key and bypass PAM authentication).

Written on 17 September 2023.
« Apache's HTTP Basic Authentication could do with more logging
Making a function that defines functions in GNU Emacs ELisp »

Page tools: View Source, View Normal, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Sun Sep 17 21:44:38 2023
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.