Pluggable Authentication Modules (PAM): Difference between revisions

From SaruWiki
Jump to navigation Jump to search
m (→‎PAM Principles: - introduction)
m (→‎control value [expression]: expounded expressions)
 
(8 intermediate revisions by the same user not shown)
Line 1: Line 1:
'''Pluggable Authentication Modules for Linux''' (Linux-PAM) is a great way to fit your Linux server with authentication-related services. However, we found their use pretty difficult, due to some aspects of PAM not being as obvious as the tutorial and howto-writers apparently expect. Thus, we here try to explain [[SaruWiki:About | ourselves]] (and you, dear reader) how PAM works.
'''Pluggable Authentication Modules for Linux''' (Linux-PAM) is a great way to fit your Linux server with [[Authentication, authorization and privileges | authentication-related]] services. However, we found their use pretty difficult, due to some aspects of PAM not being as obvious as the tutorial and howto-writers apparently expect. Thus, we here try to explain [[SaruWiki:About | ourselves]] (and you, dear reader) how PAM works.


=PAM introduction=
=PAM introduction=


==What is PAM==
==What is PAM==
Most Linux applications require authentication of some type or another. For authentication, multiple mechanisms are available, ranging from the standard Linux password through certificates all the way to smartcard readers, fingerprint readers and what have you not. To make use of one (or more) of these mechanisms, there are two main choices: either program the code required to interface with your authentication mechanism of choice, or make use of some framework that can take care of the interfacing with the authentication mechanism for you.
Most Linux applications require [[Authentication, authorization and privileges | authentication]] of some type or another. For [[Authentication, authorization and privileges | authentication]], multiple mechanisms are available, ranging from the standard Linux password through certificates all the way to smartcard readers, fingerprint readers and what have you not. To make use of one (or more) of these mechanisms, there are two main choices: either program the code required to interface with your authentication mechanism of choice, or make use of some framework that can take care of the interfacing with the authentication mechanism for you.


It will be clear that programming authentication mechanism support is not usually a smart move; for any new authentication mechanism, as well as for any change in existing ones, you need to reprogram, recompile et cetera. Connecting to a framework on the other hand is a one-time effort, requiring only extra effort if and when the framework itself changes. And here we encounter PAM, because PAM is just such a framework.
Historically, an application that required a given user to be [[Authentication, authorization and privileges | authenticated]] had to be compiled to use a specific authentication mechanism. For example, in the case of traditional UN*X systems, the identity of the user is verified by the user entering a correct password. This password, after being prefixed by a two character "salt", is encrypted (with ''crypt''). The user is then authenticated if this encrypted password is identical to the second field of the user's entry in the system password database (the ''/etc/passwd'' file). On such systems, most if not all forms of [[Authentication, authorization and privileges | privileges]] are granted based on this single authentication scheme. [[Authentication, authorization and privileges | Privilege]] comes in the form of a personal user-identifier (UID) and membership of various groups. Services and applications are available based on the personal and group identity of the user. Traditionally, group membership has been assigned based on entries in the ''/etc/group'' file.


Free after the PAM admin guide: Linux-PAM is a suite of shared libraries. Using these libraries, the local system administrator can specify how PAM-aware applications authenticate users. In other words, if an application or service is PAM-aware, then it is possible to switch between the authentication mechanism(s) it uses without (rewriting and) recompiling it. Indeed, one may entirely upgrade or reconfigure the local authentication system without touching the applications themselves. Furthermore, the administrative effort associated with authentication managemnet is reduced, since all configuration using the framework is the same for any application using it. In other words: admins have to learn only once to operate the framework, and can then deploy and configure any application using it.
This all means, that if you ever want to change something in the way users or groups are stored, then you'll have to rewrite and recompile each and every application that has the support for ''passwd'' and ''group'' hardcoded. And if you wanted to include support for some other authentication mechanism, you'd have to rewrite ''every'' application that's using authentication. Furthermore, the configuration of your authentication mechanism most likely takes place in the configuration file of each individual program. This means different configuration files, probably with different notations, syntax, et cetera. It will be clear that programming authentication mechanism support is not usually a smart move. Enter authentication frameworks.


Historically, an application that required a given user to be authenticated had to be compiled to use a specific authentication mechanism. For example, in the case of traditional UN*X systems, the identity of the user is verified by the user entering a correct password. This password, after being prefixed by a two character "salt", is encrypted (with crypt(3)). The user is then authenticated if this encrypted password is identical to the second field of the user's entry in the system password database (the /etc/passwd file). On such systems, most if not all forms of privileges are granted based on this single authentication scheme. Privilege comes in the form of a personal user-identifier (UID) and membership of various groups. Services and applications are available based on the personal and group identity of the user. Traditionally, group membership has been assigned based on entries in the /etc/group file.
Suppose we create a framework that contains multiple authentication mechanisms, each conveniently shaped in the form of separate modules and libraries. Suppose we have a clearly defined interface, so that any program can easily link to this framework. Programs can then configure their interface to the framework to choose the authentication mechanisms they want themselves, and then delegate the [[Authentication, authorization and privileges | authenticity question]] to this framework; once a user is authenticated (or not), the framework passes the "yes" (or "no") back to the program. This makes connecting to a framework a one-time effort, requiring only extra effort if and when the framework itself changes. And here we encounter PAM, because PAM is just such a framework.


It is the purpose of the Linux-PAM project to separate the development of privilege granting software from the development of secure and appropriate authentication schemes. This is accomplished by providing a library of functions that an application may use to request that a user be authenticated. This PAM library is configured locally with a system file, /etc/pam.conf (or a series of configuration files located in /etc/pam.d/) to authenticate a user request via the locally available authentication modules. The modules themselves will usually be located in the directory /lib/security or /lib64/security and take the form of dynamically loadable object files
Free after the PAM admin guide: Linux-PAM is a suite of shared libraries. Using these libraries, the local system administrator can specify how PAM-aware applications authenticate users. "PAM-aware" in this context means that the application is written to interface with the PAM framework. In other words, if an application or service is PAM-aware, then it is possible to select/combine/switch between the PAM authentication mechanism(s) you want it to use, without (rewriting and) recompiling program code. Indeed, one may entirely upgrade or reconfigure the local authentication system without touching the applications themselves. Furthermore, the administrative effort associated with authentication management is reduced, since all configuration is the same for any application using using the framework. In other words: admins have to learn only once to operate the framework, and can then deploy and configure any application using it.
 
It is the purpose of the Linux-PAM project to separate the development of [[Authentication, authorization and privileges | privilege granting]] software from the development of secure and appropriate authentication schemes. This is accomplished by providing a library of functions that an application may use to request that a user be authenticated. This PAM library is configured locally with a series of configuration files located in ''/etc/pam.d/'' (or a system file, ''/etc/pam.conf'') to authenticate a user request via the locally available authentication modules. The modules themselves will usually be located in the directory ''/lib/security'' or ''/lib64/security'' and take the form of dynamically loadable object files.


== Why use PAM ==
== Why use PAM ==
Line 18: Line 20:
= PAM Principles =
= PAM Principles =


== Invoking PAM ==
== PAM modules - in general ==
As you might expect from the previous texts, you can only use PAM if your application, service, daemon or program is "PAM-aware". This means, that it is coded and compiled to use PAM. Fortunately, in this day and age most applications, daemons et cetera are. However, this presents us with a true problem: that of ''choice''. When your application is PAM-aware, it means that your application can deletate just about any imaginable authentication-task to PAM - which YOU, the system administrator, must achieve by invoking PAM in just the right way. To this end, you must create a PAM configuration file, and fill it with just the right information as to have PAM perform all tasks that you want - and none other.
So we have these PAMs (or "PAM modules" as they're also referred to). Each module exists as a single file, usually beginning with ''pam_'' followed by a pretty descriptive name; for example ''pam_deny.so''. Each of these modules can be invoked by an application to answer a question about an authentication attempt. Furthermore, modules can be "stacked", so that you can call several modules sequentially. This makes it possible for an application to consult two different user repositories for a single login attempt: if ''both'' answer that the user is unknown, then the login attempt should probably fail, but if ''one'' of the two answers positively on the authentication request, then the user is authenticated, and the login attempt may continue. PAM has the necessary controls for this. These are named "control flags" and are treated further down.


== PAM module types ==
So our modules can answer questions we have on an authentication request. But we can have several types of questions, like "may this user log in from this particular workstation" or "after logging in, what environment variables should the user get". PAM can handle these questions as well, because there are different types of modules. Or to be more precise: an individual module can address more than one of the module types. For instance pam_unix.so has components which address four module types. Currently, there ''are'' four module types, and they are explained below.


=== module type ''auth'' ===
''auth'' type modules can answer [[Authentication, authorization and privileges |authentication requests]]. These are requests of the type "Is the user trying to log on REALLY user ''root''? Please verify using this password." But next to this, ''auth'' PAMs can also check for the application if system conditions are met, like "Is the user logging in from an acceptable device?" or "Is login currenly allowed (i.e. is the file ''/etc/nologin'' absent)?" Furthermore, this module type can also ''set'' credentials, like Kerberos tickets.


== PAM modules - in general ==
== A line of PAM configuration code ==
== PAM module types ==
=== module type ''auth'' ===
=== module type ''account'' ===
=== module type ''account'' ===
''account'' type modules verify additional attributes of the digital identity trying to authenticate; these attributes could be described as "account data". Think of things like "is the user's account not disabled?", "Is the user entitled to accessing the specific resource?" (i.e. entitlement), et cetera.
=== module type ''password'' ===
=== module type ''password'' ===
When users can make use of a certain authentication mechanism, then they usually can also make some changes for their entry in that mechanism. For example, if a user can authenticate using a username/password, then he usually can also change his password. These changes can be made through this type of PAM; there usually is one ''password''-type PAM for every authentication method that the system is fitted with.
=== module type ''session'' ===
=== module type ''session'' ===
''session'' type modules don't so much check the user login attempt, but they perform tasks that must be completed after a user has been authenticated. ''session'' type modules can load user limits, mount (home) directories, record login data for auditing purposes, or check system resources to see if there are enough resources to support the login attempt.
== PAM control flags ==
== PAM control flags ==
== A line of PAM configuration code - revisited ==
So if a PAM is called to provide an answer, how do you determine what to do with the answer? To this end, we can -and must- make use of a control flag. These are the control flags that are available, ''plus'' a more complicated ''control value'' expression:
 
=== control flag ''required'' ===
This is quite a common control flag. A ''required'' module must be successfully checked in order to allow authentication. If a ''required module'' check fails, the user is not notified until all other modules of the same module type have been checked.
 
=== control flag ''requisite'' ===
A ''requisite'' module must be successfully checked in order for the authentication to be successful. However, if a ''requisite'' module check fails, the user is notified immediately with a message reflecting the first failed ''required'' or ''requisite'' module.
 
=== control flag ''sufficient'' ===
A ''sufficient'' module check is ignored if it fails. But, if a module flagged ''sufficient'' is successfully checked, and no ''required'' flagged modules above it have failed, then no other modules of this module type are checked and the user is authenticated.
 
=== control flag ''optional'' ===
An ''optional'' module check is always ignored if it fails. And if the module check is successful, it still doesn't play a role in the overall success or failure for that module type. The only time a module flagged as ''optional'' is necessary for successful authentication, is when no other modules of that type have succeeded or failed. ONLY in this case, an ''optional'' module determines the overall PAM authentication for that module type.
 
=== control value [expression] ===
Instead of one of the "classic" control flags, it is also possible to create a more complicated expression, as summarily mentioned in the [http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/sag-configuration-file.html Linux-PAM SysAdmin guide]. The whole expression is enclosed in square brackets, with inbetween one or more expressions ''value=action''. Each ''value'' corresponds with must be one of this list: success, open_err, symbol_err, service_err, system_err, buf_err, perm_denied, auth_err, cred_insufficient, authinfo_unavail, user_unknown, maxtries, new_authtok_reqd, acct_expired, session_err, cred_unavail, cred_expired, cred_err, no_module_data, conv_err, authtok_err, authtok_recover_err, authtok_lock_busy, authtok_disable_aging, try_again, ignore, abort, authtok_expired, module_unknown, bad_item, conv_again, incomplete, and default.<br>
Each ''action'' corresponds with one of these:
* '''<number>''': if you list a number (an integer that's 1 or greater), then the effect will be that if your call to the module returns <value>, then your PAM process will skip the next <number> lines; this is a rudimentary form of flow control
* '''ignore''': when used with a stack of modules, the module's return status will not contribute to the return code the application obtains.
* '''bad''': this action indicates that the return code should be thought of as indicative of the module failing. If this module is the first in the stack to fail, its status value will be used for that of the whole stack.
* '''die''': equivalent to bad with the side effect of terminating the module stack and PAM immediately returning to the application.
* '''ok''': this tells PAM that the administrator thinks this return code should contribute directly to the return code of the full stack of modules. In other words, if the former state of the stack would lead to a return of PAM_SUCCESS, the module's return code will override this value. Note, if the former state of the stack holds some value that is indicative of a modules failure, this 'ok' value will not be used to override that value.
* '''done''': equivalent to ok with the side effect of terminating the module stack and PAM immediately returning to the application.
* '''reset''': clear all memory of the state of the module stack and start again with the next stacked module.
If you've grasped the effects of the classic control flags, then you'll see that each of these makes use of these actions. To be precise: each of the classic control flags has an equivalent expression in terms of the [...] syntax. These equivalent expressions are:
required    [success=ok  new_authtok_reqd=ok  ignore=ignore  default=bad]
requisite  [success=ok  new_authtok_reqd=ok  ignore=ignore  default=die]
sufficient  [success=done  new_authtok_reqd=done  default=ignore]
optional    [success=ok  new_authtok_reqd=ok  default=ignore]
 
== PAM module arguments ==
PAM can use "arguments" to pass information to a pluggable module during authentication. These arguments allow the PAM configuration files for particular programs to use a common PAM module, but in different ways.
 
For example, the ''pam_userdb.so'' module uses secrets stored in a Berkeley DB file to authenticate the user. Berkeley DB is an open source database system designed to be embedded in many applications to track information. The module takes a "db argument", specifying the Berkeley DB filename to use, which can be different for different services. Thus, the ''pam_userdb.so'' line in a PAM configuration file can look like this:
auth      required  /lib/security/pam_userdb.so db=path/to/file
Invalid arguments are ignored, and do not otherwise affect the success or failure of the PAM module. When an invalid argument is passed, an error is usually written to the ''/var/log/messages'' file. However, since the reporting method is controlled by the PAM module itself, you depend on the module having been written correctly to log the error to this file. Usually this is the case, but be aware that exotic PAMs might write to a different location, or not at all.
 
== PAM configuration lines==
As was already shown in the previous section, a single line of PAM configuration code consists of the four fields we've now covered, separated by whitespace. The fields are:
<module type>  <control flag>  <PAM>  <optional arguments>
Multiple arguments can be written one after another, separated by spaces (PAM simply interprets the whole rest of the line after the <PAM> as arguments). You can string lines like this after one another in a PAM configuration file, and the PAM framework will process them one after another at the time of an authentication request.
 
A little extra: we can include files into our PAM configuration file using the ''@include'' statement. Thus, the file /etc/pam.d/sshd could contain the following lines:
auth      required    pam_env.so
auth      required    pam_env.so envfile=/etc/default/locale
@include common-auth
account    required    pam_nologin.so
@include common-account
@include common-session
session    optional    pam_motd.so
session    optional    pam_mail.so standard noenv
session    required    pam_limits.so
@include common-password
The effect of this is that the files ''common-auth'', ''common-account'', ''common-session'' and ''common-passwd'' (all from directory ''/etc/pam.d/'' as well) are included into our sshd configuration. Not wholly surprisingly, file ''common-auth'' contains all common PAM authentication lines, ''common-account'' all common PAM account lines, etcetera. This makes it relatively easy to add authentication/authorisation mechanisms to our Debian server; we've only to add them to these ''common-'' files, and all services and processes will know about them.
 
We can also include modules into our PAM configuration using the substack procedure. This includes all lines of given type from the configuration file specified as an argument to this control. A call using substack would look like this:
auth      substack    common-auth
This differs from include in that evaluation of the done and die actions in a substack does not cause skipping the rest of the complete module stack, but only of the substack. Jumps in a substack also can not make evaluation jump out of it, and the whole substack is counted as one module when the jump is done in a parent stack. The reset action will reset the state of a module stack to the state it was in as of beginning of the substack evaluation.
 
== Invoking PAM ==
As you might expect from the previous texts, you can only use PAM if your application, service, daemon or program is "PAM-aware". This means, that it is coded and compiled to use PAM. Fortunately, in this day and age most applications, daemons et cetera are. However, this presents us with a true problem: that of ''choice''. When your application is PAM-aware, it means that your application can deletate just about any imaginable authentication-task to PAM - which YOU, the system administrator, must achieve by invoking PAM in just the right way. To this end, you must create a PAM configuration file, and fill it with just the right information as to have PAM perform all tasks that you want - and none other. This is because when a privilege granting application is started that is PAM aware, it activates its attachment to the PAM-API. This activation performs a number of tasks, the most important being the reading of the configuration file (more on that further on). This file list the PAMs (the "PAM modules") that will do the authentication tasks required by this application, and the appropriate behavior of the PAM-API in the event that individual PAMs fail.
 
Now there are several ways to invoke PAM, and which ones are available to us under Debian will be more fully explored in a later section, but for now let's look at one particular method, that is also available under Debian. If we create a file in ''/etc/pam.d/'' with the name of the service that we want to have PAM perform authentication for, then that can contain the necessary PAM configuration lines, and that will be the PAM configuration for that particular service. Does that make sense? No? Then let me give you an example.
 
Suppose we have some service with a name of ''sshd'', and we want that service to use PAM to check if a user that attempts to use the service is actuall allowed to do so. We then create ''/etc/pam.d/sshd'' and fill it with these two lines:
auth      required      pam_unix.so
session    optional      pam_motd.so
This means we've now configured the ''sshd'' [[OpenSSH server | SSH daemon]] to use PAM for authentication/authorization. The exact instructions we've provided PAM are: check if the name and password provided to ''sshd'' by a user attempting to log in represent a valid name/password as they occur in our local Linux user account database (''/etc/passwd''). If so, we'll allow access. The second line furthermore shows a user logging in the [[MOTD_file | Message-of-the-day]], if that message exists.
 
Think that that's easy? Almost looks like it, right? But
 
==An example: sshd configuration file ==
For a real SSH daemon, the PAM configuration file can look something like this:
auth      required      pam_env.so
auth      required      pam_env.so envfile=/etc/default/locale
auth      sufficient    pam_unix.so nullok_secure
auth      requisite      pam_succeed_if.so uid >= 1000 quiet
auth      sufficient    pam_ldap.so use_first_pass
auth      required      pam_deny.so
account    required      pam_access.so
account    sufficient    pam_unix.so
account    sufficient    pam_succeed_if.so uid < 1000 quiet
account    [default=bad success=ok user_unknown=ignore] pam_ldap.so
account    required      pam_permit.so
session    required      pam_limits.so
session    required      pam_unix.so
session    required      pam_mkhomedir.so skel=/etc/skel/ umask=0022
session    optional      pam_ldap.so
session    optional      pam_motd.so
session    optional      pam_mail.so standard noenv
session    required      pam_limits.so
password  sufficient    pam_unix.so md5 obscure min=4 nullok try_first_pass
password  sufficient    pam_ldap.so
password  required      pam_deny.so
Well that looks a bit intimidating, doesn't it? Do not worry, we'll explain everything in great detail, so we are quite sure that, once you get to the end of this document, you'll understand what files like this mean. And hopefully, you'll even be able to create or edit your own!
 
In the example, there are five lines that begin with ''auth''. This means that for any authentication request, PAM consults five modules. Let's have a look at what they do:
* ''auth      required      pam_env.so''<br>This line calls the ''pam_env.so'' PAM, which is a module that can ''only'' be called as an ''auth'' module. It is configured using the ''/etc/security/pam_env.conf'' file, and what it does is it sets a number of environmental variables for the user that tries to log in. These variables can be declared in the ''pam_env.conf'' file, and can be used to supply default values for environmental variables that are not (yet) set for the user, and to override other environmental variables for which the administrator wishes to provide different values from what the user might have configured. It's flagged ''required'', so this module must succeed or the user will be denied access. Fortunately, this module will always succeed.
* '' auth      required      pam_env.so envfile=/etc/default/locale''<br>Hey, haven't we seen this one before? It's the same ''pam_env.so'', but now it's reading an ''envfile'', so as to set the variables that are in the file ''locale''.
* '' auth      sufficient    pam_unix.so nullok_secure''<br>This calls the PAM named ''pam_unix.so'' in its authentication mode. The module is flagged ''sufficient'', so if this module recognises the user, then no other ''auth'' module will be consulted - effectively skipping the next three ''auth'' modules. See that order is important?<br>The module will check if the authentication attempt is made by a valid Unix user. The parameter nullok_secure makes sure that even login attempts by users with blank passwords fail, as long as they try to authenticate from a secure terminal
*  ''auth      requisite      pam_succeed_if.so uid >= 1000 quiet''<br>This module ''[http://linux.die.net/man/8/pam_succeed_if pam_succeed_if.so]'' brings a rudimentary form of flow control to our PAM configuration files. It can perform tests, and based on that test will cause a "success" or "fail" state to the ''auth'' section - but it WON'T return "success" or "fail" to the authentication attempt itself! Because it is here used with ''requisite'', a failure of this module would immediately be effective. The first argument (or set of arguments) is ''uid >= 1000''. This is the actual test that the module will be performing: if the user that is trying to authenticate has a user ID of LESS than 1000, the module will fail, and the ''requisite'' flag will cause PAM to perform no more ''auth'' module tests. The last argument of this module is a flag ''quiet'', which makes this module skip recording failure or success messages in the message log.<br>So what is now the effect of this module? For ordinary system users (who have an uid of 1000 or more), this module will do just nothing. But if this module is called for an authentication request by a service daemon, then the uid will be less than 1000, so ''pam_succeed_if.so'' will FAIL. The ''requisite'' control flag causes PAM to immediately end the ''auth'' stage of the login; that means all following ''auth'' modules will ONLY be processed for ordinary users, not service daemons. Neat, eh?
* ''auth      sufficient    pam_ldap.so use_first_pass''<br>This module (because of the preceding line never called when daemons try to log in) will attempt to see if the user and password that need to be authenticated are valid in your LDAP server. The ''sufficient'' control flag makes sure that if the user is NOT a valid LDAP user, nothing happens. However, if he is, AND no previous ''auth'' modules have returned an auth failure, then the PAM returns "success" and the ''auth'' phase immediately ends.
* ''auth      required      pam_deny.so''<br>This last ''auth'' module is the catchall; if no previous ''auth'' module of type ''sufficient'' has returned a success, then this module kicks in. And guess what? Called as an ''auth'' module, it ALWAYS returns "failure".
 
The compound effect of this authentication section of the example PAM configuration is this:
# Default environmental variables are set
# The ''locale'' file is read
# All valid Unix users, and Unix users with blank passwords from secure terminals, are authenticated OK
# All valid Unix users


= PAM on Debian=
= PAM on Debian=

Latest revision as of 12:51, 16 November 2008

Pluggable Authentication Modules for Linux (Linux-PAM) is a great way to fit your Linux server with authentication-related services. However, we found their use pretty difficult, due to some aspects of PAM not being as obvious as the tutorial and howto-writers apparently expect. Thus, we here try to explain ourselves (and you, dear reader) how PAM works.

PAM introduction

What is PAM

Most Linux applications require authentication of some type or another. For authentication, multiple mechanisms are available, ranging from the standard Linux password through certificates all the way to smartcard readers, fingerprint readers and what have you not. To make use of one (or more) of these mechanisms, there are two main choices: either program the code required to interface with your authentication mechanism of choice, or make use of some framework that can take care of the interfacing with the authentication mechanism for you.

Historically, an application that required a given user to be authenticated had to be compiled to use a specific authentication mechanism. For example, in the case of traditional UN*X systems, the identity of the user is verified by the user entering a correct password. This password, after being prefixed by a two character "salt", is encrypted (with crypt). The user is then authenticated if this encrypted password is identical to the second field of the user's entry in the system password database (the /etc/passwd file). On such systems, most if not all forms of privileges are granted based on this single authentication scheme. Privilege comes in the form of a personal user-identifier (UID) and membership of various groups. Services and applications are available based on the personal and group identity of the user. Traditionally, group membership has been assigned based on entries in the /etc/group file.

This all means, that if you ever want to change something in the way users or groups are stored, then you'll have to rewrite and recompile each and every application that has the support for passwd and group hardcoded. And if you wanted to include support for some other authentication mechanism, you'd have to rewrite every application that's using authentication. Furthermore, the configuration of your authentication mechanism most likely takes place in the configuration file of each individual program. This means different configuration files, probably with different notations, syntax, et cetera. It will be clear that programming authentication mechanism support is not usually a smart move. Enter authentication frameworks.

Suppose we create a framework that contains multiple authentication mechanisms, each conveniently shaped in the form of separate modules and libraries. Suppose we have a clearly defined interface, so that any program can easily link to this framework. Programs can then configure their interface to the framework to choose the authentication mechanisms they want themselves, and then delegate the authenticity question to this framework; once a user is authenticated (or not), the framework passes the "yes" (or "no") back to the program. This makes connecting to a framework a one-time effort, requiring only extra effort if and when the framework itself changes. And here we encounter PAM, because PAM is just such a framework.

Free after the PAM admin guide: Linux-PAM is a suite of shared libraries. Using these libraries, the local system administrator can specify how PAM-aware applications authenticate users. "PAM-aware" in this context means that the application is written to interface with the PAM framework. In other words, if an application or service is PAM-aware, then it is possible to select/combine/switch between the PAM authentication mechanism(s) you want it to use, without (rewriting and) recompiling program code. Indeed, one may entirely upgrade or reconfigure the local authentication system without touching the applications themselves. Furthermore, the administrative effort associated with authentication management is reduced, since all configuration is the same for any application using using the framework. In other words: admins have to learn only once to operate the framework, and can then deploy and configure any application using it.

It is the purpose of the Linux-PAM project to separate the development of privilege granting software from the development of secure and appropriate authentication schemes. This is accomplished by providing a library of functions that an application may use to request that a user be authenticated. This PAM library is configured locally with a series of configuration files located in /etc/pam.d/ (or a system file, /etc/pam.conf) to authenticate a user request via the locally available authentication modules. The modules themselves will usually be located in the directory /lib/security or /lib64/security and take the form of dynamically loadable object files.

Why use PAM

PAM Principles

PAM modules - in general

So we have these PAMs (or "PAM modules" as they're also referred to). Each module exists as a single file, usually beginning with pam_ followed by a pretty descriptive name; for example pam_deny.so. Each of these modules can be invoked by an application to answer a question about an authentication attempt. Furthermore, modules can be "stacked", so that you can call several modules sequentially. This makes it possible for an application to consult two different user repositories for a single login attempt: if both answer that the user is unknown, then the login attempt should probably fail, but if one of the two answers positively on the authentication request, then the user is authenticated, and the login attempt may continue. PAM has the necessary controls for this. These are named "control flags" and are treated further down.

PAM module types

So our modules can answer questions we have on an authentication request. But we can have several types of questions, like "may this user log in from this particular workstation" or "after logging in, what environment variables should the user get". PAM can handle these questions as well, because there are different types of modules. Or to be more precise: an individual module can address more than one of the module types. For instance pam_unix.so has components which address four module types. Currently, there are four module types, and they are explained below.

module type auth

auth type modules can answer authentication requests. These are requests of the type "Is the user trying to log on REALLY user root? Please verify using this password." But next to this, auth PAMs can also check for the application if system conditions are met, like "Is the user logging in from an acceptable device?" or "Is login currenly allowed (i.e. is the file /etc/nologin absent)?" Furthermore, this module type can also set credentials, like Kerberos tickets.

module type account

account type modules verify additional attributes of the digital identity trying to authenticate; these attributes could be described as "account data". Think of things like "is the user's account not disabled?", "Is the user entitled to accessing the specific resource?" (i.e. entitlement), et cetera.

module type password

When users can make use of a certain authentication mechanism, then they usually can also make some changes for their entry in that mechanism. For example, if a user can authenticate using a username/password, then he usually can also change his password. These changes can be made through this type of PAM; there usually is one password-type PAM for every authentication method that the system is fitted with.

module type session

session type modules don't so much check the user login attempt, but they perform tasks that must be completed after a user has been authenticated. session type modules can load user limits, mount (home) directories, record login data for auditing purposes, or check system resources to see if there are enough resources to support the login attempt.

PAM control flags

So if a PAM is called to provide an answer, how do you determine what to do with the answer? To this end, we can -and must- make use of a control flag. These are the control flags that are available, plus a more complicated control value expression:

control flag required

This is quite a common control flag. A required module must be successfully checked in order to allow authentication. If a required module check fails, the user is not notified until all other modules of the same module type have been checked.

control flag requisite

A requisite module must be successfully checked in order for the authentication to be successful. However, if a requisite module check fails, the user is notified immediately with a message reflecting the first failed required or requisite module.

control flag sufficient

A sufficient module check is ignored if it fails. But, if a module flagged sufficient is successfully checked, and no required flagged modules above it have failed, then no other modules of this module type are checked and the user is authenticated.

control flag optional

An optional module check is always ignored if it fails. And if the module check is successful, it still doesn't play a role in the overall success or failure for that module type. The only time a module flagged as optional is necessary for successful authentication, is when no other modules of that type have succeeded or failed. ONLY in this case, an optional module determines the overall PAM authentication for that module type.

control value [expression]

Instead of one of the "classic" control flags, it is also possible to create a more complicated expression, as summarily mentioned in the Linux-PAM SysAdmin guide. The whole expression is enclosed in square brackets, with inbetween one or more expressions value=action. Each value corresponds with must be one of this list: success, open_err, symbol_err, service_err, system_err, buf_err, perm_denied, auth_err, cred_insufficient, authinfo_unavail, user_unknown, maxtries, new_authtok_reqd, acct_expired, session_err, cred_unavail, cred_expired, cred_err, no_module_data, conv_err, authtok_err, authtok_recover_err, authtok_lock_busy, authtok_disable_aging, try_again, ignore, abort, authtok_expired, module_unknown, bad_item, conv_again, incomplete, and default.
Each action corresponds with one of these:

  • <number>: if you list a number (an integer that's 1 or greater), then the effect will be that if your call to the module returns <value>, then your PAM process will skip the next <number> lines; this is a rudimentary form of flow control
  • ignore: when used with a stack of modules, the module's return status will not contribute to the return code the application obtains.
  • bad: this action indicates that the return code should be thought of as indicative of the module failing. If this module is the first in the stack to fail, its status value will be used for that of the whole stack.
  • die: equivalent to bad with the side effect of terminating the module stack and PAM immediately returning to the application.
  • ok: this tells PAM that the administrator thinks this return code should contribute directly to the return code of the full stack of modules. In other words, if the former state of the stack would lead to a return of PAM_SUCCESS, the module's return code will override this value. Note, if the former state of the stack holds some value that is indicative of a modules failure, this 'ok' value will not be used to override that value.
  • done: equivalent to ok with the side effect of terminating the module stack and PAM immediately returning to the application.
  • reset: clear all memory of the state of the module stack and start again with the next stacked module.

If you've grasped the effects of the classic control flags, then you'll see that each of these makes use of these actions. To be precise: each of the classic control flags has an equivalent expression in terms of the [...] syntax. These equivalent expressions are:

required    [success=ok  new_authtok_reqd=ok  ignore=ignore  default=bad]
requisite   [success=ok  new_authtok_reqd=ok  ignore=ignore  default=die]
sufficient  [success=done  new_authtok_reqd=done  default=ignore]
optional    [success=ok  new_authtok_reqd=ok  default=ignore]

PAM module arguments

PAM can use "arguments" to pass information to a pluggable module during authentication. These arguments allow the PAM configuration files for particular programs to use a common PAM module, but in different ways.

For example, the pam_userdb.so module uses secrets stored in a Berkeley DB file to authenticate the user. Berkeley DB is an open source database system designed to be embedded in many applications to track information. The module takes a "db argument", specifying the Berkeley DB filename to use, which can be different for different services. Thus, the pam_userdb.so line in a PAM configuration file can look like this:

auth      required  /lib/security/pam_userdb.so db=path/to/file

Invalid arguments are ignored, and do not otherwise affect the success or failure of the PAM module. When an invalid argument is passed, an error is usually written to the /var/log/messages file. However, since the reporting method is controlled by the PAM module itself, you depend on the module having been written correctly to log the error to this file. Usually this is the case, but be aware that exotic PAMs might write to a different location, or not at all.

PAM configuration lines

As was already shown in the previous section, a single line of PAM configuration code consists of the four fields we've now covered, separated by whitespace. The fields are:

<module type>   <control flag>   <PAM>  <optional arguments>

Multiple arguments can be written one after another, separated by spaces (PAM simply interprets the whole rest of the line after the <PAM> as arguments). You can string lines like this after one another in a PAM configuration file, and the PAM framework will process them one after another at the time of an authentication request.

A little extra: we can include files into our PAM configuration file using the @include statement. Thus, the file /etc/pam.d/sshd could contain the following lines:

auth       required     pam_env.so
auth       required     pam_env.so envfile=/etc/default/locale
@include common-auth
account    required     pam_nologin.so
@include common-account
@include common-session
session    optional     pam_motd.so
session    optional     pam_mail.so standard noenv
session    required     pam_limits.so
@include common-password

The effect of this is that the files common-auth, common-account, common-session and common-passwd (all from directory /etc/pam.d/ as well) are included into our sshd configuration. Not wholly surprisingly, file common-auth contains all common PAM authentication lines, common-account all common PAM account lines, etcetera. This makes it relatively easy to add authentication/authorisation mechanisms to our Debian server; we've only to add them to these common- files, and all services and processes will know about them.

We can also include modules into our PAM configuration using the substack procedure. This includes all lines of given type from the configuration file specified as an argument to this control. A call using substack would look like this:

auth       substack     common-auth

This differs from include in that evaluation of the done and die actions in a substack does not cause skipping the rest of the complete module stack, but only of the substack. Jumps in a substack also can not make evaluation jump out of it, and the whole substack is counted as one module when the jump is done in a parent stack. The reset action will reset the state of a module stack to the state it was in as of beginning of the substack evaluation.

Invoking PAM

As you might expect from the previous texts, you can only use PAM if your application, service, daemon or program is "PAM-aware". This means, that it is coded and compiled to use PAM. Fortunately, in this day and age most applications, daemons et cetera are. However, this presents us with a true problem: that of choice. When your application is PAM-aware, it means that your application can deletate just about any imaginable authentication-task to PAM - which YOU, the system administrator, must achieve by invoking PAM in just the right way. To this end, you must create a PAM configuration file, and fill it with just the right information as to have PAM perform all tasks that you want - and none other. This is because when a privilege granting application is started that is PAM aware, it activates its attachment to the PAM-API. This activation performs a number of tasks, the most important being the reading of the configuration file (more on that further on). This file list the PAMs (the "PAM modules") that will do the authentication tasks required by this application, and the appropriate behavior of the PAM-API in the event that individual PAMs fail.

Now there are several ways to invoke PAM, and which ones are available to us under Debian will be more fully explored in a later section, but for now let's look at one particular method, that is also available under Debian. If we create a file in /etc/pam.d/ with the name of the service that we want to have PAM perform authentication for, then that can contain the necessary PAM configuration lines, and that will be the PAM configuration for that particular service. Does that make sense? No? Then let me give you an example.

Suppose we have some service with a name of sshd, and we want that service to use PAM to check if a user that attempts to use the service is actuall allowed to do so. We then create /etc/pam.d/sshd and fill it with these two lines:

auth       required       pam_unix.so
session    optional       pam_motd.so

This means we've now configured the sshd SSH daemon to use PAM for authentication/authorization. The exact instructions we've provided PAM are: check if the name and password provided to sshd by a user attempting to log in represent a valid name/password as they occur in our local Linux user account database (/etc/passwd). If so, we'll allow access. The second line furthermore shows a user logging in the Message-of-the-day, if that message exists.

Think that that's easy? Almost looks like it, right? But

An example: sshd configuration file

For a real SSH daemon, the PAM configuration file can look something like this:

auth       required       pam_env.so
auth       required       pam_env.so envfile=/etc/default/locale
auth       sufficient     pam_unix.so nullok_secure
auth       requisite      pam_succeed_if.so uid >= 1000 quiet
auth       sufficient     pam_ldap.so use_first_pass
auth       required       pam_deny.so
account    required       pam_access.so
account    sufficient     pam_unix.so
account    sufficient     pam_succeed_if.so uid < 1000 quiet
account    [default=bad success=ok user_unknown=ignore] pam_ldap.so
account    required       pam_permit.so
session    required       pam_limits.so
session    required       pam_unix.so
session    required       pam_mkhomedir.so skel=/etc/skel/ umask=0022
session    optional       pam_ldap.so
session    optional       pam_motd.so
session    optional       pam_mail.so standard noenv
session    required       pam_limits.so
password   sufficient     pam_unix.so md5 obscure min=4 nullok try_first_pass
password   sufficient     pam_ldap.so
password   required       pam_deny.so

Well that looks a bit intimidating, doesn't it? Do not worry, we'll explain everything in great detail, so we are quite sure that, once you get to the end of this document, you'll understand what files like this mean. And hopefully, you'll even be able to create or edit your own!

In the example, there are five lines that begin with auth. This means that for any authentication request, PAM consults five modules. Let's have a look at what they do:

  • auth required pam_env.so
    This line calls the pam_env.so PAM, which is a module that can only be called as an auth module. It is configured using the /etc/security/pam_env.conf file, and what it does is it sets a number of environmental variables for the user that tries to log in. These variables can be declared in the pam_env.conf file, and can be used to supply default values for environmental variables that are not (yet) set for the user, and to override other environmental variables for which the administrator wishes to provide different values from what the user might have configured. It's flagged required, so this module must succeed or the user will be denied access. Fortunately, this module will always succeed.
  • auth required pam_env.so envfile=/etc/default/locale
    Hey, haven't we seen this one before? It's the same pam_env.so, but now it's reading an envfile, so as to set the variables that are in the file locale.
  • auth sufficient pam_unix.so nullok_secure
    This calls the PAM named pam_unix.so in its authentication mode. The module is flagged sufficient, so if this module recognises the user, then no other auth module will be consulted - effectively skipping the next three auth modules. See that order is important?
    The module will check if the authentication attempt is made by a valid Unix user. The parameter nullok_secure makes sure that even login attempts by users with blank passwords fail, as long as they try to authenticate from a secure terminal
  • auth requisite pam_succeed_if.so uid >= 1000 quiet
    This module pam_succeed_if.so brings a rudimentary form of flow control to our PAM configuration files. It can perform tests, and based on that test will cause a "success" or "fail" state to the auth section - but it WON'T return "success" or "fail" to the authentication attempt itself! Because it is here used with requisite, a failure of this module would immediately be effective. The first argument (or set of arguments) is uid >= 1000. This is the actual test that the module will be performing: if the user that is trying to authenticate has a user ID of LESS than 1000, the module will fail, and the requisite flag will cause PAM to perform no more auth module tests. The last argument of this module is a flag quiet, which makes this module skip recording failure or success messages in the message log.
    So what is now the effect of this module? For ordinary system users (who have an uid of 1000 or more), this module will do just nothing. But if this module is called for an authentication request by a service daemon, then the uid will be less than 1000, so pam_succeed_if.so will FAIL. The requisite control flag causes PAM to immediately end the auth stage of the login; that means all following auth modules will ONLY be processed for ordinary users, not service daemons. Neat, eh?
  • auth sufficient pam_ldap.so use_first_pass
    This module (because of the preceding line never called when daemons try to log in) will attempt to see if the user and password that need to be authenticated are valid in your LDAP server. The sufficient control flag makes sure that if the user is NOT a valid LDAP user, nothing happens. However, if he is, AND no previous auth modules have returned an auth failure, then the PAM returns "success" and the auth phase immediately ends.
  • auth required pam_deny.so
    This last auth module is the catchall; if no previous auth module of type sufficient has returned a success, then this module kicks in. And guess what? Called as an auth module, it ALWAYS returns "failure".

The compound effect of this authentication section of the example PAM configuration is this:

  1. Default environmental variables are set
  2. The locale file is read
  3. All valid Unix users, and Unix users with blank passwords from secure terminals, are authenticated OK
  4. All valid Unix users

PAM on Debian

PAM modules in Debian 5.0 'Lenny'

PAM configuration files

Configuration example

Configuring PAM for LDAP