With PAM the authenticators are implemented as dynamically-linked shared library files. A single standard API is used by all authentication programs, and a configuration file read at run time determines which authenticators are actually used for each program. Thus you can revise how authentication works by just installing new modules and/or editting the configuration files. You don't need to rebuild the application programs.
PAM was originally developed by Sun for Solaris. A DCE/OSF-RFC document describes it (not completely accurately). An open source version called Linux-PAM is widely used, and can be installed on most versions of Unix, not just Linux. It is standard on recent Linux distributions, FreeBSD 3.1 or greater, and probably lots of others. A newer open source version called OpenPAM is used in FreeBSD 5.0 and later. HP-UX supports PAM, but I have no experience with their version.
The normal config file formats for Linux and Solaris PAM are slightly different, and the names of the standard modules are entirely different. There are probably interesting portability issues involved in writing PAM modules which I haven't studied. This page discusses mainly the issues involved in invoking PAM modules.
Things to be aware of:
If you're writing a conversation function, there is little you can do about this other than put in some system dependent #ifdefs, but authors of modules can write code that works under both interpretations. The simplest way is never to pass more than one prompt to the conversation function - if you need to pass more, call the conversation function more than once. A more devious trick is to store the pam_message structures in an array, then create an array of pam_message * pointers with each pointer pointing to the corresponding pam_message structure. If a pointer the pointer array is passed to the conversation function, things will work right under either interpretation.
char *pam_strerror(int errnum);Versions of Linux-PAM before 0.59 implemented it this way. However, no Solaris versions ever did, and later versions of Linux-PAM have changed too. It is now:
char *pam_strerror(pam_handle_t *pamh, int errnum);