The Surreal Horror of Pam
christine.website
christine.website
This was how things were done before PAM, and it presumed that you could twist the arms of the proprietary Unix vendors hard enough that they would give you to the source to /bin/login, which of course back then was encumbered by the AT&T Unix license, which means you needed to pay AT&T to get a Source License before you went back to twisting the arms of the proprietary Unix vendor. And if you had a multi-vendor deployment, you might need to separately customize the /bin/login for OSF/1, Solaris, AIX, HP/UX, AUX, and Irix for your large scale deployment --- since all of the proprietary Unix vendors had added their own, incompatible, "value adds" to the OS. Yelch!
The Solaris developers told me about this cool library called Pluggable Authentication Modules which they had been working on, and it was clear to me that this was the answer we were looking for. No longer would each site need to hand edit C code to customize what was supposed to happen vis-a-vis using the user's password to get Kerberos tickets, or get AFS tokens, and what might be needed for session startup such as site-specific ways of attaching the user's home directory.
So I took the idea back from the Bay Area, and I started talking to folks in the Linux community and said, this is the answer to allow us to be able to distribute advanced systems such as Yellow Pages, Kerberos, OpenAFS, etc., and when the user installs the right packages we can automatically make /bin/login do the right thing. Huzzah! Michael K. Johnson at Red Hat and I managed to recruit Andrew Morgan to be the maintainer of Linux-PAM, and it started shipping in Linux distributions in 1996. (Red Hat Linux 3.0.4, shipped in August 1996 had PAM support --- note, this is RHL 3.0.4 which is not RHEL 3. Red Hat Linux predated Red Hat Enterprise Linux.)
Although we did a clean-room reimplementation of the PAM spec, using only the man pages which the Solaris developers had provided to me, it started shipping in Linux distributions before Sun managed to ship their implementation of PAM in Solaris in 1997, even though they developed the spec and had a prototype before we had even started coding.
So the history in Christine's talk isn't quite right. PAM was not developed because of the existence of SSH. It's true that SSH was first written in 1995 as well, and the fact that it made it easier to integrate SSH was a bonus, but the primary concern that I (as a Linux developer and Kerberos Tech Lead) and Sun was most interested in was how to avoid custom, site-specific customized versions of /bin/login so we could more easily promote adoption of new technologies like Kerberos without requiring the site administrator be able to modify /bin/login, or having a combinatorial explosion of different /bin/logins for all of the different distributed computing systems which needed changes to /bin/login.
Oh, and Java was released as a Beta in 1995. The reason why PAM modules wasn't written in Java was because (a) it would have been insane to use something the size of a JVM for a critical system program like /bin/login, and (b) Sun Microsystems' marketing arm hadn't yet started promoting Java like crazy promising "write once, run^H^H^H^H debug everywhere", and claiming that Java was the right answer no matter what the question was. Also, (c) I believe that the Solaris engineers, for whom I have immense respect, had way more good taste than that. :-)
There is this feeling of an expanse, of seeing something way larger than oneself, that one experiences when this occurs.
Thank you. And thank you HN.
I feel like this comment belongs right along side the PAM source, if only to remind others that we weren't all that nutty coding it this way.
Thanks for this writeup!
It was a way to provide centralized access to User/Groups/Password and hostnames information over the network. YP was a staple of most UNIX deployments for some time, before being replaced by LDAP and DNS.
You'll find some historical context here [1].
[1] https://en.wikipedia.org/wiki/Network_Information_Service
Subscriber link: https://lwn.net/SubscriberLink/874174/693a5853d33f4b45/
Oh, and I was also in the Java group after I moved from the Systems Group and the first implementation of PAM was in 1991 if you can believe it. It definitely pre-dated Java and when I joined "Firstperson" (which was the soooper seekrit project that was developing 'oak' which was to become Java) in 1992 the target was embedded systems like TVs or set top boxes not the web.
[1] Which was called the Zeus Name Service or ZNS at the time.
%PAM-1.0
auth required pam_faillock.so preauth
auth [success=3 default=ignore] pam_unix.so try_first_pass nullok
auth [success=2 default=ignore] pam_winbind.so
-auth [success=1 default=ignore] pam_systemd_home.so
auth [default=die] pam_faillock.so authfail
auth required pam_permit.so
auth required pam_env.so
auth required pam_faillock.so authsucc
-account [success=2 default=ignore] pam_systemd_home.so
account [success=1 default=ignore] pam_winbind.so
account required pam_unix.so
account optional pam_permit.so
account required pam_time.so
-password [success=2 default=ignore] pam_systemd_home.so
password [success=1 default=ignore] pam_winbind.so
password required pam_unix.so sha512 shadow
password optional pam_permit.so
session required pam_limits.so
session required pam_unix.so
session required pam_winbind.so
session optional pam_permit.so
The syntax is quite terse and like nothing else. No idea why it insists on wasting characters though. auth could be au, account: ac, password: pa and session: se. Why on earth put pam_ on the front of the modules and .so on the end. Tut!The winbind stuff is configured elsewhere in several places - /etc/samba/smb.conf and /etc/security/pam_winbind.conf (obviously).
In general it is good enough to keep a spare virtual console logged in as root whilst you play with PAM. Otherwise get some experience with say Gentoo and Arch and the like. Then you will have rather good system rescue skills. Keep a System Rescue CD/USB to hand ... boot, mount /, /boot, /dev, /sys etc, chroot ... fix whatever you broke and then reboot. Rinse, repeat. Or spin up a VM and snapshot/checkpoint it before experimenting.
Because it uses dynamic linking of the shared libraries used for authentication.
The config file doesn't need to worry about the nuts and bolts. All of the PAM modules are named pam_xxxxx.so so the configuration file only needs to specify the name, ie the xxxxx. Also some parts of the configuration are abbreviated, eg: auth and other bits are not, eg: password.
Those "[success = n" things mean "skip this number of lines if this invocation succeeds. It's a pretty mad "if f(x) == true goto n" statement.
Begrudgingly I have to say that despite the oddness of it all, it is reasonably easy to follow what is going on when you understand the rules.
I suspect you have awoken the Dark Ones (errr the PAM devs)
PAM is pretty odd but not that bad!
I would rather type a few extra characters so that it is blindingly obvious that "pam_foo.so" in the config file will load code from pam_foo.so in the filesystem.
Typing only "foo" would remove a little clutter, but it would also increase my cognitive load a little.
> I would rather type a few extra characters so that it is blindingly obvious that "pam_foo.so" in the config file will load code from pam_foo.so in the filesystem.
> Typing only "foo" would remove a little clutter, but it would also increase my cognitive load a little.
Exactly. Sometimes magic like what the GP wants is evil. It sub-optimally trades a steeper learning curve for some too-small convenience. It's like buying a shiny new penny for a dollar.
That's bonk. PAM significantly simplified the ecosystem - at the time there were dozens specialized hack upon hack upon hack login suits such as
* login-otp
* login-shadow
* login-skey
* des-login
* login-rsa-otp
that implemented some of the functionality for some of the manipulations, sometimes on the same system. Some of them already used shared libraries and some of those shared libraries already used the names. Specifying loading paths fully was considered a bad idea because of how subsystems were installed on the deployed servers. The decisions about name collisions ( pam_ prefix ), following Sun's original specs ( some of the names of stages ), etc were made from those constraints. It needed to be deployable on the top of the existing login systems where the users would not be locked out because of some sort of a conflict.
PAM significantly simplified that messy ecosystem while being extremely system, which is why it won and remains the integral part of the ecosystem for twenty seven years.
But hey, this is open source -- don't like it? Rewrite it in Rust! Just remember that first you would need to make sure that Rust exists on all the platforms PAM has been used on.[0]
[0] Pam-static was created as during the first few releases Linux/Alpha did not have support for shared libraries. So there it was, Pluggable Authentication Modules which on Alpha did not have dynamic loaders.
>> It's like buying a shiny new penny for a dollar.
> That's bonk. PAM significantly simplified the ecosystem...
That's not what I was talking about at all. I wasn't talking about PAM itself, but the commenter's impulse to use an abbreviated name of the module in the config rather than the module's actual name. PAM did that right by using the full name in my opinion.
My theory is that the authors of both didn't know about parser generators.
auth required /etc/whatever/my_pam_module.soThis led Ubuntu to create /usr/share/pam-configs which have files like
Name: SSS authentication
Default: yes
Priority: 128
Auth-Type: Primary
Auth:
[success=end default=ignore] pam_sss.so use_first_pass
Auth-Initial:
[success=end default=ignore] pam_sss.so forward_pass
Account-Type: Additional
Account:
sufficient pam_localuser.so
[default=bad success=ok user_unknown=ignore] pam_sss.so
Session-Type: Additional
Session-Interactive-Only: yes
Session:
optional pam_sss.so
Password-Type: Primary
Password:
sufficient pam_sss.so use_authtok
Password-Initial:
sufficient pam_sss.so
to try and make something declarative. RHEL/CentOS went the authconfig route but both ended up saying to never ever touch anything in /etc/pam.d“Wow this could be really useful” you might think. “Where “is it documented?”
https://wiki.ubuntu.com/PAMConfigFrameworkSpec
“But where is the reference, how do I know what directives are even available?”
…
Ubuntu has /etc/pam.d/common-auth which looks like the main module to define your intent. Strip out the comments and see what you have left - read the comments first! Copy the whole lot somewhere and login to another console with root rights. Now you can play.
What would you like to do?
As I've learned the hard way, at least keep a root shell open somewhere if you do...
Inventing bizarre and unusual turing complete configuration languages is a GNU/Linux hobby - just look at UDev reals.
I have more experience with PAM than most Linux admins, I think, and yet I still have to refer to man pages or Google every time I need to do something with it. The keywords and options are already basically nonsense.
It's almost as if PAM were developed before the invention of the if-then-else construct in the 1950s.[0]
[0]: https://github.com/e-n-f/if-then-else/blob/master/if-then-el...
Since ordering was critical in PAM config files, and it was presumed that typical sysadmins weren't going to be editing PAM config files, it had to ship with mentions of every single package that might require a PAM config file. And this is what caused it to get super complex --- and not very well documented, since the presumption was that only distro-engineers needed to understand it, and tech writers have always been underappreciated and underpaid for as long as I can remember in the field (and probably longer).
.so does give you some info (namely, that it's an external shared object), but I'm not entirely certain how that alone gives you any better idea what's going on: that shared object could do literally anything. You need to know how PAM uses the shared object to know what's going on.
The filename, obviously.
An example of such a test program can be found at http://pamtester.sourceforge.net. I haven't tried using it myself, but I have taken a quick look at the source code, and it appears legit, if a bit over-engineered. Creating a test driver program which links against libpam really isn't all that complex, especially if you use the "configure the program by editing /tmp/foo.c and recompiling" methodology. :-)
I make use of the pam_u2f.so module to require a FIDO2 fob AND password for my logins.
I even configued it to have backup fobs allowed in case I lose my primary, secondary, etc. (which I store in exceedingly secure spaces).
It's great that somebody would need both my password & my FIDO2 to access my laptop and there's no clear indication at login that the FIDO2 is required first if it isn't plugged in.
Because it maps user IDs to names so that you can see names in `ls -l` and `who` and such.
The article's tagged as satire, I'm not sure you should be expecting good logic and sources
I'd think this would cover all the bases. But what do I know, I just wrote the talk.
And if anyone's still offended: my comments are partially satirical too. There, I'm immunized.
I made an incorrect statement? Don't worry, I _meant_ to get that part wrong.
This part here that was insightful and you enjoyed? I meant that part 100%.
maybe Xe can address their comments in a followup, perhaps even after consulting with them a bit on the project itself
[0] assuming your nsswitch says so
So it was world readable.
It's really just "accountdetails" now, with the real password living in a different file.
Of course I remember the old "yp" system that would blast the whole file with passwords over the network to anyone who asked. The old Unix guys had a lot of faith in their hash algorithms.
Is that something your PAM module(s) would need to handle?
e.g. Now that you're authenticated via the IP, how do we authorize the login?
Either way this needs a lot of work to be super secure and useful, right now it's mostly a proof of concept that has taken FOREVER because debugging PAM is so painful. There's more to come in the future, but I fully expect this to be a bit of an uphill sell to people. Writing stuff that mucks with the authentication stack is rightfully kinda scary, and there will need to be a lot of security and subject matter expert review. I expect this to be a slow burn, but good god I would love to see it happen.
Alternatively, now that it's authenticated securely enabling password for authz could work maybe?
Actually using it would be a long ways off, but I don't think conceptually it's a hard sell. I'm interested in making authentication easier for my developers lately, and also in dodging some key management in favor of accounts people already have at work. Right now, I'm evaluating Tailscale for VPN access, since I find myself supporting developers without (yet) any remote access to them or any endpoint management crap on their machines, and walking them through configuring WireGuard has proven painful (more painful than I expected, which maybe sounds dumb). (Incidentally, ‘Tailscale’ is a name I remembered as a possible VPN solution because I think your blog is good.)
One of the things that's occurred to me during my testing is that if it could be as stunningly easy to configure as Tailscale, it'd be great to use some kind of SSH gateway that plugged into everyone's SSO instead of managing a bunch of SSH keys. Plus, being able to have admins confirm privilege escalation requests might make it easier for me to convince some higher-ups to give the developers I support sudo rights.
There are products that support things like this, but I think the use case is a natural extension of what people are likely already using Tailscale for, and it would be a killer feature to integrate.
PS - great talk. Security/auth is tricky, and it's never clear when I'm doing the right thing or something _supremely dumb_. Thanks for putting yourself out there.
I don't do that. No one ever does that. Normal people don't keep a picturebook of authentication mechanisms that they need to cycle through. All the non-/etc/password PAM modules that some clueless IT ever forced on me (for as long as it took me to root and purge them from existence) to hook up with some network service just end up breaking your user account. The highlight is when they don't even authenticate with the network because they instead cache credentials.
If I understand Tailscale correctly a PAM module that knows which machine it is running on and the username logging in will allow them to decide on a per-machine level if the current client is allowed to go there (to this account), so it could bring their authentication game to a whole other level.
Not sure if they should do that (or if anyone should do that) but it's an interesting thing to think about. Even if it's just to hide away some of the pain that are the PAMs/LDAP Groups of this world.
not sure if i totally agree with the immutability argument for /etc, but i suppose that's more about protecting against errant devs abusing sudo privs to do weird things rather than securing things against malicious actors.
This is the best way. The key is making sure that somewhere you're managing user ids, cause cleaning up after assigning the same uid to different users on different systems gets icky.
one of the reasons to prefer flat files is if getpwnam and friends start to hang or return inconsistent results, the system will become unresponsive or misbehave in all sorts of amusing ways.
I think the craft-marketplace Etsy is actually named after that pronunciation of "etc"
("scuzzy")
So, when you say USB, I reach for my SCSI manuals.. :)
The latin word "et" ("and"), and the single abbreviated letter C.
I actually write "etc." (in prose) as "et c." for this reason.
> I've never met anyone who pronounced /home "aich oh em ee" or /dev "dee ee vee"
I think fewer syllables is _exactly_ why people don't say those other pronunciations for /home and /dev. If there was a reasonable way to pronounce /etc as a single syllable, I'm almost 100% certain that would be how people pronounce it.
(I "grew up", as it were, pronouncing fsck as "fossick" which seems appropriately onomatopoeic.)
1. dynamic provisioning: in /etc/pam.d/sshd, add "pam_mkhomedir.so" as a session module. Easy as that.
2. domain SSH auth: in /etc/ssh/sshd_config, add an "AuthorizedKeysCommand". This is a program you'd write, that should accept a username on the command line (the username passed by the SSH client), and should emit a list of valid SSH pubkeys for that user. This slots in place of the default SSHd behavior of reading $USER_HOME/.ssh/authorized_keys — `cat ~/.ssh/authorized_keys` is essentially the "built-in" AuthorizedKeysCommand.
The key insight with step 2 is that you can make this authentication step serve double-duty as an authorization step. If you don't want a particular domain user on this system, you can just have your AuthorizedKeysCommand emit no acceptable SSH keys for that user, leaving them unable to authenticate.
If and when the AuthorizedKeysCommand authenticates+authorizes you (by whatever means), the pam_mkhomedir module will turn around and make you a local POSIX user and home directory to correspond with your domain account (with whatever username the client provided, so make sure to validate that), and you'll end up logged into said local POSIX account.
(You won't have a password configured / any way to convince PAM to re-authenticate you while logged in; but that's fine for most things — you can still fork out new processes, start new sessions of screen/tmux, etc. The only consideration is sudo access: just ensure that the sudo group has the NOPASSWD attribute set in /etc/sudoers.)
This is essentially how GCP's "IAM-based" OS Login (https://cloud.google.com/compute/docs/instances/managing-ins...) works: there's an AuthorizedKeysCommand (/usr/bin/google_authorized_keys) that fetches the SSH keys recorded in IAM for the SSH username [= escaped GCP IAM ID] you're claiming to be logging in as, and emits them if-and-only-if that GCP IAM ID has the OS-Login IAM role; and then PAM's dynamic provisioning kicks in and handles the rest.
Also, for a maybe-clearer example, I wrote my own little toy AuthorizedKeysCommand, that uses Github as an authentication domain (https://github.com/tsutsu/github-auth3). You tell it a Github org, and then anyone in that Github org can log in by with their Github username + an SSH key registered to that Github user. (Again, it checks the username for org membership, and only if the user is in the org, prints the SSH pubkeys registered to the user.)
=====
Of course, compared to doing everything in PAM, this approach isn't as elegant: every "domain user" that logs into the system ends up being able to leave junk there—at the very least, they end up with a UID permanently allocated to them in /etc/passwd.
You can ameliorate this slightly by NFS-mounting /home; or even individual homedirs as-needed using autofs.
(Intriguingly, macOS ships by default with autofs preconfigured to mount homedirs for OpenDirectory users, despite Server.app not exposing any such OpenDirectory capability — presumably there's some Apple-internal setup for their workstations that seeks parity with Windows' Roaming User Profiles.)
I think this is incorrect. If you want encryption that's built into NFS (instead of a kernel VPN like IPsec/Wireguard or tunneling) then there is krb5p.
https://whyistheinternetbroken.wordpress.com/2017/07/24/onta...
The only question left for me really is the authorization of which users you're allowed to login as.
It is pretty comical when people who do not know basics of how the system permissions work wax about authentication and authorization used by the same systems.
There was a time when it was considered ok to have the salted and MD5-hashed password available to all users.
Then people learned to bruteforce the password and so the hash itself was moved to shadow file but passwd was left because a lot of platform software and scripts depended on it.
This may shock people nowadays but back then this was fine. I remember having to explain to the users (and sometimes loosing) the need to have a password at all.
"but passwd was left because a lot of platform software and scripts depended on it."
The confusion rests on the name of the file I think. If it were named "accounts" (closer to its use) people wouldn't be too surprised it was readable. Then shadow could have been named "passwords."
With a user who's lost multiple accounts to weak passwords.
Or should I say, the same weak password. Which they still use in some contexts....
I'm of the opinion that allowing users free choice in passwords is no longer viable. At a bare minimum reuse of any known-compromised password must be rejected.
Seasons 2-4 are pure gold, but season 5 and on is dog water.