Whole document tree
    

Whole document tree

The Linux-PAM Module Writers' Guide: What can be expected by the module Next Previous Contents

2. What can be expected by the module

Here we list the interface that the conventions that all Linux-PAM modules must adhere to.

2.1 Getting and setting PAM_ITEMs and data

First, we cover what the module should expect from the Linux-PAM library and a Linux-PAM aware application. Essesntially this is the libpam.* library.

Setting data

Synopsis:

extern int pam_set_data(pam_handle_t *pamh,
                        const char *module_data_name,
                        void *data,
                        void (*cleanup)(pam_handle_t *pamh,
                                        void *data, int error_status) );

The modules may be dynamically loadable objects. In general such files should not contain static variables. This and the subsequent function provide a mechanism for a module to associate some data with the handle pamh. Typically a module will call the pam_set_data() function to register some data under a (hopefully) unique module_data_name. The data is available for use by other modules too but not by an application.

The function cleanup() is associated with the data and, if non-NULL, it is called when this data is over-written or following a call to pam_end() (see the Linux-PAM Application Developers' Guide).

The error_status argument is used to indicate to the module the sort of action it is to take in cleaning this data item. As an example, Kerberos creates a ticket file during the authentication phase, this file might be associated with a data item. When pam_end() is called by the module, the error_status carries the return value of the pam_authenticate() or other libpam function as appropriate. Based on this value the Kerberos module may choose to delete the ticket file (authentication failure) or leave it in place.

The error_status may have been logically OR'd with either of the following two values:

PAM_DATA_REPLACE

When a data item is being replaced (through a second call to pam_set_data()) this mask is used. Otherwise, the call is assumed to be from pam_end().

PAM_DATA_SILENT

Which indicates that the process would prefer to perform the cleanup() quietly. That is, discourages logging/messages to the user.

Getting data

Synopsis:

extern int pam_get_data(const pam_handle_t *pamh,
                        const char *module_data_name,
                        const void **data);

This function together with the previous one provides a method of associating module-specific data with the handle pamh. A successful call to pam_get_data will result in *data pointing to the data associated with the module_data_name. Note, this data is not a copy and should be treated as constant by the module.

Note, if there is an entry but it has the value NULL, then this call returns PAM_NO_MODULE_DATA.

Setting items

Synopsis:

extern int pam_set_item(pam_handle_t *pamh
                        , int item_type
                        , const void *item
                        );

This function is used to (re)set the value of one of the item_types. The reader is urged to read the entry for this function in the Linux-PAM application developers' manual.

In addition to the items listed there, the module can set the following two item_types:

PAM_AUTHTOK

The authentication token (password). This token should be ignored by all module functions besides pam_sm_authenticate() and pam_sm_chauthtok(). In the former function it is used to pass the most recent authentication token from one stacked module to another. In the latter function the token is used for another purpose. It contains the currently active authentication token.

PAM_OLDAUTHTOK

The old authentication token. This token should be ignored by all module functions except pam_sm_chauthtok().

Both of these items are reset before returning to the application. When resetting these items, the Linux-PAM library first writes 0's to the current tokens and then free()'s the associated memory.

The return values for this function are listed in the Linux-PAM Application Developers' Guide.

Getting items

Synopsis:

extern int pam_get_item(const pam_handle_t *pamh
                        , int item_type
                        , const void **item
                        );

This function is used to obtain the value of the specified item_type. It is better documented in the Linux-PAM Application Developers' Guide. However, there are three things worth stressing here:

  • Generally, if the module wishes to obtain the name of the user, it should not use this function, but instead perform a call to pam_get_user() (see section below).
  • The module is additionally privileged to read the authentication tokens, PAM_AUTHTOK and PAM_OLDAUTHTOK (see the section above on pam_set_data()).
  • The module should not free() or alter the data pointed to by *item after a successful return from pam_get_item(). This pointer points directly at the data contained within the *pamh structure. Should a module require that a change is made to the this ITEM it should make the appropriate call to pam_set_item().

The conversation mechanism

Following the call pam_get_item(pamh,PAM_CONV,&item), the pointer item points to a conversation-function that provides limited but direct access to the application. The purpose of this function is to allow the module to prompt the user for their password and pass other information in a manner consistent with the application. For example, an X-windows based program might pop up a dialog box to report a login failure. Just as the application should not be concerned with the method of authentication, so the module should not dictate the manner in which input (output) is obtained from (presented to) to the user.

The reader is strongly urged to read the more complete description of the pam_conv structure, written from the perspective of the application developer, in the Linux-PAM Application Developers' Guide.

The pam_response structure returned after a call to the pam_conv function must be free()'d by the module. Since the call to the conversation function originates from the module, it is clear that either this pam_response structure could be either statically or dynamically (using malloc() etc.) allocated within the application. Repeated calls to the conversation function would likely overwrite static memory, so it is required that for a successful return from the conversation function the memory for the response structure is dynamically allocated by the application with one of the malloc() family of commands and must be free()'d by the module.

If the pam_conv mechanism is used to enter authentication tokens, the module should either pass the result to the pam_set_item() library function, or copy it itself. In such a case, once the token has been stored (by one of these methods or another one), the memory returned by the application should be overwritten with 0's, and then free()'d.

The return values for this function are listed in the Linux-PAM Application Developers' Guide.

Getting the name of a user

Synopsis:

extern int pam_get_user(pam_handle_t *pamh, 
                        const char **user,
                        const char *prompt);

This is a Linux-PAM library function that returns the (prospective) name of the user. To determine the username it does the following things, in this order:

  • checks what pam_get_item(pamh, PAM_USER, ... ); would have returned. If this is not NULL this is what it returns. Otherwise,
  • obtains a username from the application via the pam_conv mechanism, it prompts the user with the first non-NULL string in the following list:
    • The prompt argument passed to the function
    • What is returned by pam_get_item(pamh,PAM_USER_PROMPT, ... );
    • The default prompt: ``Please enter username: ''

By whatever means the username is obtained, a pointer to it is returned as the contents of *user. Note, this memory should not be free()'d by the module. Instead, it will be liberated on the next call to pam_get_user(), or by pam_end() when the application ends its interaction with Linux-PAM.

Also, in addition, it should be noted that this function sets the PAM_USER item that is associated with the pam_[gs]et_item() function.

The return value of this function is one of the following:

  • PAM_SUCCESS - username obtained.
  • PAM_CONV_AGAIN - converstation did not complete and the caller is required to return control to the application, until such time as the application has completed the conversation process. A module calling pam_get_user() that obtains this return code, should return PAM_INCOMPLETE and be prepared (when invoked the next time) to recall pam_get_user() to fill in the user's name, and then pick up where it left off as if nothing had happened. This procedure is needed to support an event-driven application programming model.
  • PAM_CONV_ERR - the conversation method supplied by the application failed to obtain the username.

Setting a Linux-PAM environment variable

Synopsis:

extern int pam_putenv(pam_handle_t *pamh, const char *name_value);

Linux-PAM (0.54+) comes equipped with a series of functions for maintaining a set of environment variables. The environment is initialized by the call to pam_start() and is erased with a call to pam_end(). This environment is associated with the pam_handle_t pointer returned by the former call.

The default environment is all but empty. It contains a single NULL pointer, which is always required to terminate the variable-list. The pam_putenv() function can be used to add a new environment variable, replace an existing one, or delete an old one.

  • Adding/replacing a variable
    To add or overwrite a Linux-PAM environment variable the value of the argument name_value, should be of the following form:
    name_value="VARIABLE=VALUE OF VARIABLE"
    
    Here, VARIABLE is the environment variable's name and what follows the `=' is its (new) value. (Note, that "VARIABLE=" is a valid value for name_value, indicating that the variable is set to "".)
  • Deleting a variable
    To delete a Linux-PAM environment variable the value of the argument name_value, should be of the following form:
    name_value="VARIABLE"
    
    Here, VARIABLE is the environment variable's name and the absence of an `=' indicates that the variable should be removed.

In all cases PAM_SUCCESS indicates success.

Getting a Linux-PAM environment variable

Synopsis:

extern const char *pam_getenv(pam_handle_t *pamh, const char *name);

This function can be used to return the value of the given variable. If the returned value is NULL, the variable is not known.

Listing the Linux-PAM environment

Synopsis:

extern char * const *pam_getenvlist(pam_handle_t *pamh);

This function returns a pointer to the entire Linux-PAM environment array. At first sight the type of the returned data may appear a little confusing. It is basically a read-only array of character pointers, that lists the NULL terminated list of environment variables set so far.

Although, this is not a concern for the module programmer, we mention here that an application should be careful to copy this entire array before executing pam_end() otherwise all the variable information will be lost. (There are functions in libpam_misc for this purpose: pam_misc_copy_env() and pam_misc_drop_env().)

2.2 Other functions provided by libpam

Understanding errors

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

    This function returns some text describing the Linux-PAM error associated with the argument errnum. If the error is not recognized ``Unknown Linux-PAM error'' is returned.

Planning for delays

  • extern int pam_fail_delay(pam_handle_t *pamh, unsigned int micro_sec)

    This function is offered by Linux-PAM to facilitate time delays following a failed call to pam_authenticate() and before control is returned to the application. When using this function the module programmer should check if it is available with,

    #ifdef HAVE_PAM_FAIL_DELAY
        ....
    #endif /* HAVE_PAM_FAIL_DELAY */
    

    Generally, an application requests that a user is authenticated by Linux-PAM through a call to pam_authenticate() or pam_chauthtok(). These functions call each of the stacked authentication modules listed in the Linux-PAM configuration file. As directed by this file, one of more of the modules may fail causing the pam_...() call to return an error. It is desirable for there to also be a pause before the application continues. The principal reason for such a delay is security: a delay acts to discourage brute force dictionary attacks primarily, but also helps hinder timed (cf. covert channel) attacks.

    The pam_fail_delay() function provides the mechanism by which an application or module can suggest a minimum delay (of micro_sec micro-seconds). Linux-PAM keeps a record of the longest time requested with this function. Should pam_authenticate() fail, the failing return to the application is delayed by an amount of time randomly distributed (by up to 25%) about this longest value.

    Independent of success, the delay time is reset to its zero default value when Linux-PAM returns control to the application.


Next Previous Contents