Linux DevCenter    
 Published on Linux DevCenter (http://www.linuxdevcenter.com/)
 See this if you're having trouble printing code examples


Writing PAM-Capable Applications, Part Two

by Jennifer Vesperman
04/18/2002

PAM stands for Pluggable Authentication Modules, a system for separating authentication mechanisms from the application.

If an application is PAM-enabled, the system administrator is responsible for determining the authentication methods used, and PAM is responsible for performing the authentication. This lets the application developer concentrate on writing the main application -- and ensures that the application isn't made out-of-date solely because of an outdated authentication schema.

This is Part Two of a two-part series on writing PAM-capable applications. In Part One, we provided necessary background information and support mechanisms, and outlined the conversation mechanism which the application must provide to let the module communicate with the user.

This part covers how to call PAM authentication, account management, session management, and token-changing functions. It also covers response codes, setting credentials, and supplying a default configuration for your application.

Unless otherwise stated, the definitions for functions and types described in this article are in <security/pam_appl.h>.

pam_start()

This function initializes PAM and provides the application with a PAM handle, which is used in the rest of the functions to uniquely identify this instance of the application.

The service name parameter is the unique PAM name of the application, used in the PAM configuration files.

The username can be NULL if not known, and the module will call back later, using the conversation function, to request it.

The pam_conversation parameter is the pointer to the conversation structure. The pamh parameter is a pointer which will be filled with the PAM handle.

The function returns PAM_SUCCESS if it succeeds, and one of several failures codes if it doesn't.

pam_handle_t *pamh=NULL;
extern int pam_start(const char *service_name, const char *user,
                     const struct pam_conv *pam_conversation,
                     pam_handle_t **pamh);

Authentication

Calling this function asks PAM to call the modules' authentication methods. This authenticates the user, using the conversation function to interact with the user. If it succeeds, it returns PAM_SUCCESS. Failure is indicated with any of a number of flags.

The Linux Web Server CD Bookshelf

Related Reading

The Linux Web Server CD Bookshelf
By O'Reilly Media, Inc.

Authentication is usually the first of the four module types to be called.

This function is usually called with the flag PAM_DISALLOW_NULL_AUTHTOK. If this flag is set and the authentication database has no stored token for the username, the authentication fails.

extern int pam_authenticate(pam_handle_t *pamh, int flags);

Account

The account management function verifies that the user account is valid, and that the user is permitted to start a session at this time. PAM account modules are available to limit accounts by time, number of users, Unix-like "nologin" blocks, and a variety of other methods.

Account is usually called after authentication, and before the session begins.

An account module will return PAM_NEW_AUTHTOK_REQD if the authentication token has expired, which allows the application to require the user to create a new authentication token. If you receive this return value, you may wish to call pam_chauthtok(), then try the account again.

It returns PAM_SUCCESS if the account is valid, and any of a number of flags to indicate failure.

extern int pam_acct_mgmt(pam_handle_t *pamh, int flags);

Setting Credentials

After the user is authenticated and the account is verified as currently usable, many authentication methods require the authenticator to set credentials. Credentials might be group membership in a Unix system, or a Kerberos ticket.

The application should call this function before the session is started.

extern int pam_setcred(pam_handle_t *pamh, int flags);

Session

These functions open and close authenticated sessions. The close session function can be called from a different application, provided the application has the PAM handle.

extern int pam_open_session(pam_handle_t *pamh, int flags);
extern int pam_close_session(pam_handle_t *pamh, int flags);

Password

pam_chauthtok changes authentication tokens, using the conversation function to interact with the user.

Calling this function with the flag PAM_CHANGE_EXPIRED_AUTHTOK will instruct it to only change the token if the token has actually expired. I recommend doing this if the call is triggered by the account module returning PAM_NEW_AUTHTOK_REQD.

This module type is usually called after the session is started, but it can be called immediately following account management, to refresh expired authentication tokens.

extern int pam_chauthtok(pam_handle_t *pamh, const int flags);

pam_end()

This function closes the PAM application. Call it when you no longer need the PAM handle, to allow the modules to be called and perform their cleanups -- freeing memory, wiping data structures clean, and disabling tokens.

The pam_status parameter indicates whether PAM was successful or not, which helps modules determine what cleanup has to be done. It can be useful to use one variable to receive the return value of all PAM calls, and bracket most PAM calls with if (retval == PAM_SUCCESS). You can then call pam_end with the return value as the pam_status argument.

extern int pam_end(pam_handle_t *pamh, int pam_status);

Failure Codes

All PAM functions return either PAM_SUCCESS or one of the following failure codes. It is worth noting that I have had PAM functions return failure codes not listed in the documentation.

The PAM function pam_strerror() displays the text string associated with each failure code.

extern const char *pam_strerror(pam_handle_t *pamh, int errnum);

Listed failure codes are:

PAM_ABORT
Usually means the PAM handle has been corrupted.



PAM_ACCT_EXPIRED
The user's account is out-of-date and he or she may not access the system.



PAM_AUTH_ERR
Authentication error.

PAM_AUTHINFO_UNAVAIL
One or more modules could not access the authentication information, perhaps due to hardware failure.

PAM_AUTHTOK_DISABLE_AGING
One or more modules has had token-aging disabled.

PAM_AUTHTOK_ERR
One or more modules could not read the new authentication token.

PAM_AUTHTOK_LOCK_BUSY
The authentication token could not be changed because of a lock.

PAM_AUTHTOK_RECOVERY_ERR
The old authentication token could not be retrieved.

PAM_BAD_ITEM
A variable could not be set/deleted because it was undefined, inaccessible, or not currently set.

PAM_BUF_ERR
Memory allocation failure.

PAM_CRED_ERR
One or more modules could not set the user's credentials.

PAM_CRED_EXPIRED
The user's credentials have expired.

PAM_CRED_INSUFFICIENT
The application is not permitted to authenticate the user, due to insufficient credentials.

PAM_CRED_UNAVAIL
One or more modules cannot read the user's credentials.

PAM_MAXTRIES
One or more authentication modules has reached its maximum number of retries. Do not continue to attempt to authenticate this user.

PAM_NEW_AUTHTOK_REQD
The authentication token has expired and should be renewed.

PAM_PERM_DENIED
This usually means permission denied, but sometimes means that a required parameter was a NULL pointer.

PAM_SYSTEM_ERR
Usually means an invalid PAM handle.

PAM_TRY_AGAIN
One or more modules could not update the authentication token, therefore none of the tokens were updated.

PAM_USER_UNKNOWN
The user is not known to one or more modules (of this type).

Additional Functions

This function is in <security/pam_misc.h>. Linux-PAM provides a function to securely duplicate strings. A copy of the parameter is returned. If there isn't enough memory to duplicate the string, a NULL is returned.

extern char *x_strdup(const char *s)

Default Configuration

In your application's installation script, check for a file with your service name in /etc/pam.d. If it doesn't exist, create one with an appropriate default PAM-authentication sequence for your program. You can find an acceptable default PAM configuration, with explanations, in /etc/pam.d/login in the default installation of many Linux systems. Another alternative is:

auth       requisite  pam_securetty.so
auth       required   pam_unix.so
account    required   pam_unix.so
session    required   pam_unix.so
password   required   pam_cracklib.so retry=3 minlen=6 difok=3
password   required   pam_unix.so use_authtok nullok md5

If a file with your service name exists, you may have a service-name clash with another application, or the file may have been installed by a previous version of your application.

Caveats and Gotchas

Final Words

This article has touched on the most critical aspects of writing PAM-capable applications. For additional details, please do see the Application Developer's Guide.

Further Reading

Jennifer Vesperman is the author of Essential CVS. She writes for the O'Reilly Network, the Linux Documentation Project, and occasionally Linux.Com.


Return to the Linux DevCenter.

Copyright © 2009 O'Reilly Media, Inc.