Fun with PAM scripts
Recently I was given the requirement to help build a data exporter tool that would fit with a customer’s existing process which uses SFTP (no comment). Rather than get into the nasty business of managing a whole separate set of credentials, we thought it would be handy if we could get the regular OpenSSH SFTP daemon to authenticate against our existing authentication platform.
By default SFTP uses the UNIX PAM (Pluggable Authentication Module) authentication system, so as a proof of concept I decided to see what the simplest possible PAM setup I could come up with that would actually work.
It had to:
- Allow the user to log in if the username and password were valid
- Deny access if the username and password were invalid
- Create a new account (by way of
useradd
) so that users would have a valid home directory once they were allowed in
I decided to run the test using login
because that is the simplest auth process that I’m aware of on Linux (you can basically run it on the command line and see results immediately).
Here’s the /etc/pam.d/login
file that I ended up with, stripped of every unnecessary functional line but retaining all important:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
And here’s /usr/sbin/pam_verify
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Obviously you should definitely NOT do this on any machine that’s exposed to the Internet in any way.
Caveats
Actually, this ended up being the biggest problem…
It was easy enough to get a simple service like login
to defer to my script for all of its authentication requirements, but it turns out that the more-complicated (and ultimately more important) OpenSSH actually does two kinds of authentication on every connection:
- NSS - To verify that the user exists, get their user ID, and locate their home directory, and
- PAM - To verify the user’s password
Unfortunately there doesn’t appear to be any way to completely skip the NSS phrase, and I was unable to find a similarly open-ended module for libnss
that would simply pass a username to an external program and wait for a response, and I wasn’t exactly comfortable writing a module myself given my extremely limited experience writing libraries in C.