Skip to main content

Anatomy of a Linux Pluggable Authentication Modules (PAM) configuration file

Ever wonder how Pluggable Application Modules are configured in Linux? Learn the basics here.
Travel adapters

In a previous article, I described the flow of an application calling PAM libraries for authentication at a very high level. In this article, we will walk through the configuration files for a local sudo command.

When using sudo, we switch users and do something. This privilege change requires verification that we are who we say we are and are allowed to perform the given action. The /etc/sudoers file controls who can do what, but the process still calls PAM for any authentication checks. As a part of these calls, the process identifies itself, and then libpam looks for a matching configuration file in the /etc/pam.d directory.

$ cat /etc/pam.d/sudo
#Type       ReturnCode   Modules       Options
auth       include      system-auth
account    include      system-auth
password   include      system-auth
session    optional revoke
session    required
session    include      system-auth

Like many other *.d directories, package management may add or remove a file from this directory. The sudo RPM adds the /etc/pam.d/sudo file.

$ rpm -qf /etc/pam.d/sudo

An upstream version might have a variety of entries, but this distribution-provided package includes a configuration file that has several include statements to the common /etc/pam.d/system-auth file which is supplied by the pam package.

Contents of the system-auth file on RHEL8

Configuration file fields

The first field in this file identifies a Type of call made to PAM. Lines of the same type are grouped together. There are four types: auth, account, password, and session. Look at man pam for a description of each type.

The second field is known as the ReturnCode. This field lets PAM know how to handle the results of the module test. Return codes indicate whether a test is required or optional. The codes might also be used to indicate the line is not a module test with options but rather the name of another configuration file with additional checks. A full description of the return codes is found in man pam.conf, and the most common ones are discussed later in this article.

The rest of the line contains the module name and options for that module. The module name must match a module available in the /etc/lib64/security directory. The options may be different depending on the type of call made. Some modules only perform tests for some types of calls. Use the man page for each module to see examples and learn about the uses and options available.

The order of entries within a type of call matters. This is mostly due to how the return codes are processed, but in some cases because of a module action. When libpam receives a "done" or "die" message, it reports the overall result back to the calling process.

The configuration for sudo has several include lines. These lines tell libpam to include all lines of given type from the configuration file specified. There is also a substack option, which is similar in how it includes lines from a given configuration file, but on a "done" or "die," it reports back to the substack instruction instead of the original process calling libpam. Older versions of PAM had other variations on how other configuration files were included. For example, when I started exploring PAM almost 20 years ago, there was a specific module called with the configuration files as an argument. The keyword "include" was not valid for the ReturnCode field.

Return codes in the central configuration files

The /etc/pam.d/sudo file shown earlier is pretty short. Three of the four call types have only an include of another file. The /etc/pam.d/system-auth file is more typical of a configuration file, with many checks for each type of call.

$ cat /etc/pam.d/system-auth
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth        required
auth        sufficient nullok try_first_pass
auth        requisite uid >= 1000 quiet_success
auth        sufficient forward_pass
auth        required

The required keyword is perhaps the most common. It indicates that the module must pass the check for an overall pass to be handed back to the application. However, even on a failure, the following lines in that type will still be checked. This is a long time practice of not sharing any reason for an authentication failure. On systems 20 years ago, it might have been possible to guess how the authentication failed by how long it took to fail.

The requisite keyword is similar to required in that the check must pass, but in the case of a failure, it returns a "die" message. Requisite lets libpam know that the following lines will not be checked, and to inform the calling process of the overall results—in this case, a failure.

The sufficient keyword is almost the opposite of requisite. On success, a "done" message is returned, and libpam goes ahead and sends its overall results back to the calling application. Other results from this module are ignored, and checking continues.

The sufficient keyword is common when there could be multiple ways of verifying a criterion. For example, when verifying a password, the user might be defined in the local /etc/passwd and /etc/shadow files, or they might only be defined in a central system accessed with sssd. The pam_unix module checks the local files. If there is success, there is no need to continue and check the centralized services. However, if the user is not defined locally, we do not want to record a failure, we want to ignore the result and try the pam_sss module. Since the sufficient keyword never truly fails, it is common to add a required pam_deny line after a series of sufficient lines. The pam_deny module always fails much like the /bin/false executable.

The optional keyword is similar to sufficient in that it ignores any failures. However, on success, it acts more like the required keyword by setting an "ok" value and continuing to perform any additional checks.

Since both requisite and sufficient can be exit points from the stack of modules, the order in the configuration file is important. Lines after those keywords may or may not get executed.

Complex return codes

Beyond the simple keyword syntax, complex return codes are defined with key-value pairs inside square brackets. The /etc/pam.d/system-auth file has a sample in the session section of the more complex syntax.

$ cat /etc/pam.d/system-auth
session     optional revoke
session     required
-session     optional
session     [success=1 default=ignore] service in crond quiet use_uid
session     required

You can find the complex syntax matching each keyword in the pam.conf man page. The - shown above is also defined in the man page. It indicates that logging can be skipped if the module is not installed on the system.

What's next?

Now that we can walk through when a call and exit occurs with libpam, the next step is to better understand the use case for each module. Most modules have a man page that explains the use and shows examples of lines that should appear in the pam.d configuration files. Some of the modules also reference supplemental files in the /etc/security directory. Those files are well commented and also often have their own man page. The pam_pwquality and pam_limits modules are good examples to get started with.

Wrap up

In the next article, I will walk through some of the changes made using the authconfig utility. If you want to jump ahead to editing files yourself and you have a Red Hat Learning Subscription, check out the chapter on PAM in the Red Hat Security: Linux in Physical, Virtual, and Cloud (RH415) course.

[ Free online course: Red Hat Enterprise Linux technical overview. ]

Topics:   Linux  
Author’s photo

Susan Lauber

Susan Lauber is a Consultant and Technical Trainer with her own company, Lauber System Solutions, Inc. More about me

Try Red Hat Enterprise Linux

Download it at no charge from the Red Hat Developer program.