The first thing to keep in mind about Pluggable Authentication Modules (PAM) is that it is not an application or protocol itself. Rather, this is a collection of libraries that applications may have been compiled to utilize. If an application is PAM-enabled, the security policy of that application can be configured by a system administrator without modifying or upgrading the application itself. Many Linux tools, especially daemons and servers, are PAM-enabled.
A quick way to check if a given tool is probably PAM-enabled is to use
ldd to check which libraries it uses. For example, I might wonder whether my login utility is PAM-enabled:
Listing 13. Is my login PAM-enabled?
% ldd /bin/login | grep libpam libpam.so.0 => /lib/libpam.so.0 (0xb7fa8000) libpam_misc.so.0 => /lib/libpam_misc.so.0 (0xb7fa5000)
The use of
login does not fully guarantee that the PAM facilities are actually being used and used correctly by this tool, but it is a good suggestion. Likewise, perhaps I wonder similarly about my Apache and FTP servers:
Listing 14. How about Apache and FTP servers?
% ldd /usr/sbin/apache2 | grep libpam % ldd /bin/login | grep libpam libpam.so.0 => /lib/libpam.so.0 (0xb7fa8000) libpam_misc.so.0 => /lib/libpam_misc.so.0 (0xb7fa5000)
So I know my particular Apache installation is not PAM-enabled (though versions are available that include this).
To check more thoroughly if PAM is fully working with a given tool, you can create a PAM configuration file for the program. For example, to test the login utility, you might create a file /etc/pam.d/login (but notice that it probably already exists on your system with a more meaningful setting, so do not delete the existing copy):
Listing 15. Checking login for PAM with etc/pam.d/login
auth required pam_permit.so auth required pam_warn.so
Now running a properly PAM-enabled
login will let anyone login, but it will log the action to the system log. If syslog shows an entry, PAM is enabled for this application. Readers will notice that this is about the worst configuration you could invent for
login since it gives anyone shell access. Having noticed that, be warned that PAM should be configured with a certain caution.
PAM works with two different types of configuration files. Most
libpam.so libraries are compiled in the "use the better one if available, but settle for the worse one" mode. However, you might also have a PAM library compiled as "use the better one, but also check the worse one." Let me explain that.
The currently preferred way to configure PAM is with files in the directory /etc/pam.d/ that are named the same as the service whose security they describe. An older and less preferred style used a single file, /etc/pam.conf, to set security policy for all applications. From a maintainability point of view, the per-application configuration files are just easier to work with and may also be symlinked to "duplicate" a security policy from one application in another. Both configuration styles look basically the same. The single /etc/pam.conf file contains lines of the form:
Listing 16. Both configuration files contain
<service> <module-type> <control-flag> <module-path> <args>
In per-application configuration files, the first field is omitted since it is already the same as the filename. In the older style, the test-only login configuration we saw would look like:
Listing 17. /etc/pam.conf
login auth required pam_permit.so login auth required pam_warn.so
<module-type> field may have one of four values:
account(non-authentication permissions based on system of user status),
session(perform actions before/after service used), and
password(update user authentication tokens).
<control-flag> field is used to "stack" modules which allows you rather subtle control of when a method is required, whether it's performed at all, and when some other fallback is acceptable. Its options are
I will discuss these in the next panel.
<module-path> we have seen in our examples; it names a shared library either in the expected module location if no path is given or at an explicit location if it starts with a "/". For example, in Listing 17, you might have specified
<args> might be anything, depending on what a particular module needs to configure its operation, though a few generic arguments should be supported by most PAM modules. Notice that PAM modules are extensible. If someone writes a new PAM module, you can simply drop it into /lib/security and all your applications can use it once their configuration file is updated to indicate that.
To see how the
<control-flag> works, let's develop an example that is moderately complex. First thing we should do is create a special OTHER service. If this exists and no PAM policy is defined for a service, OTHER's policy is used. A safe default might be like Listing 18:
Listing 18. /etc/pam.d/other
auth required pam_warn.so auth required pam_deny.so @include safe-account @include safe-password @include safe-session
In this example, an attempt at authentication is first logged to syslog and is then denied. The
@include statements just include contents from elsewhere, such as /etc/pam.d/safe-account and friends, where these "safe" definitions might contain similar warn-then-deny instructions for the other
Now let's configure access for our hypothetical classified-db application. Being rather concerned about access, for a user to use this application, he or she needs to provide either a matched retinal print or a fingerprint and also enter a password. The password, however, might be stored in either the local /etc/passwd and /etc/shadow configurations or available via one of two outside database servers.
None of the security modules I use in this example actually exist (to my knowledge), except
pam_unix.so which is old-fashioned UNIX-style password access.
Listing 19. /etc/pam.d/classified-db
auth optional pam_unix.so auth optional pam_database1.so try_first_pass auth optional pam_database2.so use_first_pass auth requisite pam_somepasswd.so auth sufficient pam_fingerprint.so master=file1 7points auth required pam_retinaprint.so
The flow through this configuration is modestly complex. First we try password authentication by three
optional module types. Since these are
optional, failing one does not stop the authentication process nor satisfy it. The standard UNIX authentication password is tried first (the user is prompted to enter a password). After that, we check passwords in
database1, but we first use the generic module argument
try_first_pass to see if the UNIX password is the same one in the database; only if it is not do we prompt for an additional password. For
database2 however, we only try to authenticate using the UNIX password that the user entered (generic argument
Having checked against some
optional password systems, we use a hypothetical
pam_somepasswd.so that needs to determine whether any of the earlier password checks succeeded (perhaps using a semaphore; but how that is done is left open for hypothetical modules). The point is that since this check is
requisite, if it fails no further authentication is done and the failure is reported.
The final two authentication checks (if they are reached) are
sufficient. That is, satisfying one of them returns an overall success status to the calling application. So first we try a fingerprint check using
pam_fingerprint.so (notice some hypothetical arguments are passed to the module). Only if this fails -- maybe because of the absence of a fingerprint scanner as well as for a bad fingerprint -- is the retinal scan attempted. Likewise, if the retinal scan succeeds, that is
sufficient. However, just to demonstrate all the flags, we actually use
required here which means that even if retinal scanning succeeds, we would continue checking other methods (but no more exist in this example, so
sufficient would do the same thing).
There is also a way of specifying more fine-tuned compound flags for the
<control-flag> using bracketed
[value1=action1 ...] sets, but the basic keywords usually suffice.