PAM is a very powerful and flexible framework. Applications that require authentication must be aware of PAM. But most basic applications and utilities in the UNIX and Linux world have been migrated. If you are an application programmer and your application requires authentication, you might wish to dig into the possibilities of PAM.
You can find modules for almost any situation, or maybe a combination of modules can solve your problem. But still you might end up in the situation where you cannot find a suitable module. In this article, you will learn how to develop your own modules.
The PAM runtime library has a well-defined API (Application Programming Interface). The PAM API is to a large extent the same on every UNIX and Linux operating system. Only a small number of differences exist, but any programmer can make a portable work-around. The differences are primarily related to the conversation function discussed later in this article. Linux-PAM provides one as a library function while other PAM implementations require the programmer to develop a conversation function. The pamtester utility provides a conversation function, which might be applicable to other applications.
The example application presented here is very simple. It can store and retrieve data (strings) in a simple (GNU DBM) database. In order to gain access, authentication through the PAM system is required. The idea is that the system administrator can control the access as he or she wishes by configuring PAM in a suitable fashion. This application is called vault.
The figure below outlines how a typical application uses PAM for authentication. Most of the usage is straightforward; the application calls a set of well-defined functions, which creates, operates on, and destroys data structures related to PAM. But PAM applies a little trick: the modules can call back to the application in order to retrieve user-related data.
In order to call PAM functions in your applications you must include the pam_appl.h header file. The sample application includes two:
The second header file is special to Linux-PAM and it is related to a text-based conversation function and a few other utility functions.
Opening and Closing a PAM Session
Any PAM session begins with creating and initializing a data structure. The data structure (C-type) is called pam_handle_t. During the application run time, it is required to hold one variable of this data structure. It contains all relevant data about the PAM session.
The creation of the data structure is equivalent to opening a PAM session. The data structure is initialized by the function pam_start. Four parameters must be supplied when calling the pam_start function. In the sample application the call to pam_start is:
retval = pam_start(“vault”, user, &conv, &pamh);
The first parameter is the service name. It is a simple text string, and if the application programmer permits it, the service name can be set by the user instead of hard-coding the service name in the application. In the sample application the service name is set to vault precisely at the call, and at this point the PAM runtime will try to find the configuration file associated with the service (/etc/pam.d/vault in this example) or the appropriate lines in the /etc/pam.conf file. The second parameter is the user name. The standard C runtime library provides the getlogin function, which returns the user’s login name as a text string.
The third parameter is a pointer to the conversation function, which is, the function that takes care of the callbacks from the modules. We will return to the conversation function shortly. The fourth and last parameter is a pointer to the PAM handling data structure (actually, a pointer to a pointer). The call to pam_start returns an integer. If the return value is PAM_SUCCESS, the initializing of the PAM handler was as it should have been. Linux-PAM provides—as defined in the pam_appl.h header file—a conversation function, while other PAM implementations require the application programmer to develop conversation functions.
When the application does not need the PAM handling data structure, it can destroy it by calling the pam_end function. This is typically just before the application is to stop executing. In the sample application, the call to pam_end at the end of the main program is as follows:
The argument retval is carried along from the last call to the PAM runtime, and depending on the return value of the previous call, PAM might have to shut down a PAM session differently.
Authenticating the User
When an application has initialized the PAM handling data structure, the next step is to authenticate the user. Since the service name and the user name are set by the call to pam_start, the authentication can be done by a simple call to the function pam_authenticate. The call is typically as simple as:
retval = pam_authenticate(pamh, 0);
The first parameter is the PAM handling data structure while the second parameter is optional flags. 0 (Zero) means silence authentication but others flags might be valid depending on the PAM implementation. The return value (stored in the variable retval above), is set to PAM_SUCCESS if the user is authenticated. If the user is it not known to PAM, the return value is PAM_UNKNOWN_USER, while a general authentication failure will lead to PAM_AUTH_ERR. In the case of Linux-PAM, the only flag is PAM_DISALLOW_NULL_AUTHTOK which will lead the return value PAM_AUTH_ERR if the user is not known to PAM. In order to authenticate a user for a particular service, the auth management group cannot be empty, that is the auth stack must have at least one module. If there are no modules the return value will be PAM_AUTHINFO_UNAVAIL.
Account Health Check
It is one thing to authenticate the user, but it is another thing to say whether the user is allowed to use the account. A number of issues influence the health of an account. For example, an account can be expired or the user may not currently be allowed to log in. The PAM function pam_acct_mgmt is used to check the health of the requested account. The simple call to the function is as follows:
retval = pam_acct_mgmt(pamh, 0);
The second parameter can be set to PAM_SILENT, which suppresses any messages from the PAM runtime, or to PAM_DISALLOW_NULL_AUTHTOK in order to require an authentication token. The flag has the same effect as for the pam_authenticate function.
Manipulating the PAM Handling Data Structure
In the sample application, the user name is set at the time of the call to the pam_start function, but this might not be always possible, so you need a function to set any piece of data. PAM data should not be accessed directly, so PAM provides methods for storing and retrieving the data items. The function’s name is pam_set_item.
Many types of items are used by PAM; the table overleaf summarizes the most important types. A complete list can be found in the Linux-PAM documentation and the Open Group’s single-sign on service (see http://www.opengroup.org/pubs/catalog/p702.htm for details).