If you're using Netscape, turn style sheets off.
Busy day today, though I was tired and didn't get as much done as I'd hoped. After messing around with Makefiles last night to get pam_ssh(8) into the static PAM library (which Ruslan Ermilov ended up fixing for me), I decided to tackle login(1), which, judging from the amount of mail I get about it, is one of the PAM applications most in need of cleanup.
Those whom the gods will destroy, they first make proud... Although I did expect some trouble with login(1), it proved even more perverse than I anticipated. The goal here is to move as much of login(1) as possible into pam_unix(8)—or other modules, as appropriate. Some of it is simple enough; for instance, I added a pam_login_access(8) module that enforces login.access(5) rules. But much of it is not obvious at all, as login(1) was written long before PAM, and developed over time into a mini-PAM of its own, as support for stuff like login.access(5) and login.conf(5) got added. I now have the enviable (ha, ha) task of unravelling it all and moving bits and pieces into different PAM modules... I estimate that I got about halfway through that today, and I might get done tomorrow, though I'll have several days of testing and review ahead of me before I can commit my changes.
Oh my, oh my. Significant portions of the underworld broke loose this weekend over some ill-conceived commits to, amongst other items, the pam_opie(8) module. I never intended it to go so far—I should have made more allowance to the language barrier. At least it wasn't for nothing; OPIE and PAM now work just fine together.
Below is a short explanation of how OPIE works, and a summary of the hurdles that had to be overcome to get it to play nice with PAM. Many thanks to Andrey Chernov and Mark Murray for their assistance in resolving these issues.
OPIE (One-time Passwords In Everything) is the successor to S/Key; it's a challenge-response system that generates non-reusable passwords which can be transmitted in plaintext with no fear of replay attacks.
To use OPIE, a user must first run opiepasswd(1) and type in a passphrase of your choice to generate an OPIE key which is stored locally. The next time the user attempts to log in—provided OPIE is properly set up—she will get an OPIE challenge instead of a password prompt. A second utility, opiekey(1), once provided with that challenge and your passphrase, will generate a response in the form of a short nonsensical phrase of English words. Note that opiekey(1) is stateless—the response is purely a function of the challenge and your passphrase—so you can run it on any machine you trust (your PalmPilot, for instance) whenever you need to log in. Beware that although the same challenge won't be reissued once you've answered it (even incorrectly), the passphrase is a single point of failure, so a trojaned opiekey(1) is still dangerous.
In addition to /etc/opiekeys, which holds the users' OPIE keys, OPIE's behavior is governed by two files: /etc/opieaccess and ~/.opiealways. The former lists hosts that are sufficiently trusted that fixed-password login is allowed from them (even over unencrypted protocols), while the latter, if it exists, prevents the use of fixed passwords even from trusted hosts (at the individual user's discretion).
The perceptive reader will already have noticed one major issue with OPIE: it tries to do PAM's work in PAM's place.
The reason for this, obviously, is that OPIE predates PAM (or rather, the wide acceptance of PAM), and that OPIE was intended to be called directly from the application, not through an abstraction layer like PAM. However, we can't just dismiss this, as admins already familiar with OPIE will expect /etc/opieaccess and ~/.opiealways to work as they did before PAM.
One solution would be to have pam_opie(8) return one of three values: PAM_SUCCESS on successful OPIE authentication; PAM_AUTH_ERR if OPIE authentication failed and the remote host is not trusted, or the user has a ~/.opiealways file; and PAM_IGNORE if OPIE authentication failed, but the remote host is trusted and the user does not have a ~/.opiealways file. The problem is that no PAM control flag supports these semantics. Linux-PAM has a long control flag syntax which would make it possible, but it's not compatible with Solaris PAM, and I don't want to strengthen our dependence on Linux-PAM implementation details.
Instead, the solution I chose was to introduce a new PAM module, pam_opieaccess(8), which enforces the traditional semantics for /etc/opieaccess and ~/.opiealways (provided the user has an OPIE key). By marking pam_opie(8) as sufficient and inserting pam_opieaccess(8) right below it with a requisite control flag, we ensure that the chain will be broken if the OPIE configuration does not allow non-OPIE authentication, and OPIE authentication failed. The pam_opieaccess(8) module simply returns PAM_AUTH_ERR in cases where further authentication is not allowed, and PAM_IGNORE in all other cases.
I also had to fix a bug in login(1), which didn't set PAM_RHOST when running on a local tty. However, though it's not documented anywhere, various bits of PAM, and particularly various PAM modules, always expect PAM_RHOST to be set, so I changed login(1) to set it to the local hostname, like su(1) does.
I also made a patch for the ProFTPD port to make it use the ftpd service (which is what ftpd(8) uses) instead of ftp. For the life of me, I can't understand why we should have two practically identical PAM policies, the only difference being one letter in the name, for two equivalent applications that never run simultaneously on the same system. I haven't heard back from the port's maintainer yet, but I don't think he'll object.
Finally, I fixed the PAM library's make(1) magic so the static library now contains all modules, including pam_ssh(8), which was tricky because it has many dependencies.
Not a bad day's work, if I may say so myself...
It's been a long time—almost an entire month—since the last entry. The holidays got in the way, of course, carving a large chunk out of December and early January, and I've been working like mad on a variety of projects (including condb, parts of which may evolve into FreeBSD's next PR system). I'm starting to feel like my head is about to blow... Quite a familiar feeling, unfortunately.
I was planning to have the PAM article done by the end of last year, but such, apparently, was not its fate. I've been working on it on and off, and it has grown a bit, but there are still three whole sections and (at least) one appendix missing, plus various odds and ends throughout the text. The article is currently my top priority; when it's done, I'll start work on moving stuff like setusercontext(3) into modules and PAMifying passwd(1) and friends. What I'll do after that is an open question; there's plenty to do, but I don't know yet in what order I'll do it.