The Linux-PAM Module Writers' Guide <author>Andrew G. Morgan, <tt>morgan@transmeta.com</tt> <date>DRAFT v0.59 1997/10/17 <abstract> This manual documents what a programmer needs to know in order to write a module that conforms to the <bf/Linux-PAM/ standard. It also discusses some security issues from the point of view of the module programmer. </abstract> <toc> <sect>Introduction <sect1> Synopsis <p> <tscreen> <verb> #include <security/pam_modules.h> gcc -fPIC -c pam_module-name.c ld -x --shared -o pam_module-name.so pam_module-name.o -lpam </verb> </tscreen> <sect1> Description <p> <bf/Linux-PAM/ (Pluggable Authentication Modules for Linux) is a library that enables the local system administrator to choose how individual applications authenticate users. For an overview of the <bf/Linux-PAM/ library see the <bf/Linux-PAM/ System Administrators' Guide. <p> A <bf/Linux-PAM/ module is a single executable binary file that can be loaded by the <bf/Linux-PAM/ interface library. This PAM library is configured locally with a system file, <tt>/etc/pam.conf</tt>, to authenticate a user request via the locally available authentication modules. The modules themselves will usually be located in the directory <tt>/usr/lib/security</tt> and take the form of dynamically loadable object files (see dlopen(3)). Alternatively, the modules can be statically linked into the <bf/Linux-PAM/ library; this is mostly to allow <bf/Linux-PAM/ to be used on platforms without dynamic linking available, but the two forms can be used together. It is the <bf/Linux-PAM/ interface that is called by an application and it is the responsibility of the library to locate, load and call the appropriate functions in a <bf/Linux-PAM/-module. <p> Except for the immediate purpose of interacting with the user (entering a password etc..) the module should never call the application directly. This exception requires a "conversation mechanism" which is documented below. <sect>What can be expected by the module <p> Here we list the interface that the conventions that all <bf/Linux-PAM/ modules must adhere to. <sect1>Getting and setting <tt/PAM_ITEM/s and <em/data/ <p> First, we cover what the module should expect from the <bf/Linux-PAM/ library and a <bf/Linux-PAM/ <em/aware/ application. Essesntially this is the <tt/libpam.*/ library. <sect2> Setting data <p> Synopsis: <tscreen> <verb> 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) ); </verb> </tscreen> <p> The modules may be dynamically loadable objects. In general such files should not contain <tt/static/ variables. This and the subsequent function provide a mechanism for a module to associate some data with the handle <tt/pamh/. Typically a module will call the <tt/pam_set_data()/ function to register some data under a (hopefully) unique <tt/module_data_name/. The data is available for use by other modules too but <em/not/ by an application. <p> The function <tt/cleanup()/ is associated with the <tt/data/ and, if non-<tt/NULL/, it is called when this data is over-written or following a call to <tt/pam_end()/ (see the Linux-PAM Application Developers' Guide). <p> The <tt/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 <tt/pam_end()/ is called by the module, the <tt/error_status/ carries the return value of the <tt/pam_authenticate()/ or other <tt/libpam/ function as appropriate. Based on this value the Kerberos module may choose to delete the ticket file (<em/authentication failure/) or leave it in place. <p> (*This paragraph is currently under advisement with Sun*) The <tt/error_status/ may have been logically OR'd with either of the following two values: <p> <descrip> <tag><tt/PAM_DATA_REPLACE/</tag> When a data item is being replaced (through a second call to <tt/pam_set_data()/) this mask is used is used. Otherwise, the call is assumed to be from <tt/pam_end()/. <tag><tt/PAM_DATA_SILENT/</tag> Which indicates that the process would prefer to perform the <tt/cleanup()/ quietly. That is, discourages logging/messages to the user. </descrip> <sect2> Getting data <p> Synopsis: <tscreen> <verb> extern int pam_get_data(const pam_handle_t *pamh , const char *module_data_name , const void **data ); </verb> </tscreen> <p> This function together with the previous one provides a method of associating module-specific data with the handle <tt/pamh/. A successful call to <tt/pam_get_data/ will result in <tt/*data/ pointing to the data associated with the <tt/module_data_name/. Note, this data is <em/not/ a copy and should be treated as <em/constant/ by the module. <p> Note, if there is an entry but it has the value <tt/NULL/, then this call returns <tt/PAM_NO_MODULE_DATA/. <sect2> Setting items <p> Synopsis: <tscreen> <verb> extern int pam_set_item(pam_handle_t *pamh , int item_type , const void *item ); </verb> </tscreen> <p> This function is used to (re)set the value of one of the <tt/item_type/s. The reader is urged to read the entry for this function in the <bf/Linux-PAM/ application developers' manual. <p> In addition to the <tt/item/s listed there, the module can set the following two <tt/item_type/s: <p> <descrip> <tag><tt/PAM_AUTHTOK/</tag> The authentication token (password). This token should be ignored by all module functions besides <tt/pam_sm_authenticate()/ and <tt/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. <tag><tt/PAM_OLDAUTHTOK/</tag> The old authentication token. This token should be ignored by all module functions except <tt/pam_sm_chauthtok()/. </descrip> <p> Both of these items are reset before returning to the application. When resetting these items, the <bf/Linux-PAM/ library first writes <tt/0/'s to the current tokens and then <tt/free()/'s the associated memory. <p> The return values for this function are listed in the <bf>Linux-PAM</bf> Application Developers' Guide. <sect2> Getting items <p> Synopsis: <tscreen> <verb> extern int pam_get_item(const pam_handle_t *pamh , int item_type , const void **item ); </verb> </tscreen> <p> This function is used to obtain the value of the specified <tt/item_type/. It is better documented in the <bf/Linux-PAM/ Application Developers' Guide. However, there are three things worth stressing here: <itemize> <item> Generally, if the module wishes to obtain the name of the user, it should not use this function, but instead perform a call to <tt/pam_get_user()/ (see section <ref id="pam-get-user" name="below">). <item> The module is additionally privileged to read the authentication tokens, <tt/PAM_AUTHTOK/ and <tt/PAM_OLDAUTHTOK/ (see the section above on <tt/pam_set_data()/). <item> The module should <em/not/ <tt/free()/ or alter the data pointed to by <tt/*item/ after a successful return from <tt/pam_get_item()/. This pointer points directly at the data contained within the <tt/*pamh/ structure. Should a module require that a change is made to the this <tt/ITEM/ it should make the appropriate call to <tt/pam_set_item()/. </itemize> <sect2>The <em/conversation/ mechanism <p> Following the call <tt>pam_get_item(pamh,PAM_CONV,&item)</tt>, the pointer <tt/item/ points to a <em/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. <p> The reader is strongly urged to read the more complete description of the <tt/pam_conv/ structure, written from the perspective of the application developer, in the <bf/Linux-PAM/ Application Developers' Guide. <p> The <tt/pam_response/ structure returned after a call to the <tt/pam_conv/ function must be <tt/free()/'d by the module. Since the call to the conversation function originates from the module, it is clear that either this <tt/pam_response/ structure could be either statically or dynamically (using <tt/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 <tt/malloc()/ family of commands and <em/must/ be <tt/free()/'d by the module. <p> If the <tt/pam_conv/ mechanism is used to enter authentication tokens, the module should either pass the result to the <tt/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 <tt/0/'s, and then <tt/free()/'d. <p> The return values for this function are listed in the <bf>Linux-PAM</bf> Application Developers' Guide. <sect2>Getting the name of a user<label id="pam-get-user"> <p> Synopsis: <tscreen> <verb> extern int pam_get_user(pam_handle_t *pamh , const char **user , const char *prompt ); </verb> </tscreen> <p> This is a <bf/Linux-PAM/ library function that returns the (prospective) name of the user. To determine the username it does the following things, in this order: <itemize> <item> checks what <tt/pam_get_item(pamh, PAM_USER, ... );/ would have returned. If this is not <tt/NULL/ this is what it returns. Otherwise, <item> obtains a username from the application via the <tt/pam_conv/ mechanism, it prompts the user with the first non-<tt/NULL/ string in the following list: <itemize> <item> The <tt/prompt/ argument passed to the function <item> What is returned by <tt/pam_get_item(pamh,PAM_USER_PROMPT, ... );/ <item> The default prompt: ``Please enter username: '' </itemize> </itemize> <p> By whatever means the username is obtained, a pointer to it is returned as the contents of <tt/*user/. Note, this memory should <em/not/ be <tt/free()/'d by the module. Instead, it will be liberated on the next call to <tt/pam_get_user()/, or by <tt/pam_end()/ when the application ends its interaction with <bf/Linux-PAM/. <p> Also, in addition, it should be noted that this function sets the <tt/PAM_USER/ item that is associated with the <tt/pam_[gs]et_item()/ function. <sect2>Setting a Linux-PAM environment variable <p> Synopsis: <tscreen> <verb> extern int pam_putenv(pam_handle_t *pamh, const char *name_value); </verb> </tscreen> <p> <bf/Linux-PAM/ (0.54+) comes equipped with a series of functions for maintaining a set of <em/environment/ variables. The environment is initialized by the call to <tt/pam_start()/ and is <bf/erased/ with a call to <tt/pam_end()/. This <em/environment/ is associated with the <tt/pam_handle_t/ pointer returned by the former call. <p> The default environment is all but empty. It contains a single <tt/NULL/ pointer, which is always required to terminate the variable-list. The <tt/pam_putenv()/ function can be used to add a new environment variable, replace an existing one, or delete an old one. <p> <itemize> <item>Adding/replacing a variable<newline> To add or overwrite a <bf/Linux-PAM/ environment variable the value of the argument <tt/name_value/, should be of the following form: <tscreen> <verb> name_value="VARIABLE=VALUE OF VARIABLE" </verb> </tscreen> Here, <tt/VARIABLE/ is the environment variable's name and what follows the `<tt/=/' is its (new) value. (Note, that <tt/"VARIABLE="/ is a valid value for <tt/name_value/, indicating that the variable is set to <tt/""/.) <item> Deleting a variable<newline> To delete a <bf/Linux-PAM/ environment variable the value of the argument <tt/name_value/, should be of the following form: <tscreen> <verb> name_value="VARIABLE" </verb> </tscreen> Here, <tt/VARIABLE/ is the environment variable's name and the absence of an `<tt/=/' indicates that the variable should be removed. </itemize> <p> In all cases <tt/PAM_SUCCESS/ indicates success. <sect2>Getting a Linux-PAM environment variable <p> Synopsis: <tscreen> <verb> extern const char *pam_getenv(pam_handle_t *pamh, const char *name); </verb> </tscreen> <p> This function can be used to return the value of the given variable. If the returned value is <tt/NULL/, the variable is not known. <sect2>Listing the Linux-PAM environment <p> Synopsis: <tscreen> <verb> extern char * const *pam_getenvlist(pam_handle_t *pamh); </verb> </tscreen> <p> This function returns a pointer to the entire <bf/Linux-PAM/ environment array. At first sight the <em/type/ of the returned data may appear a little confusing. It is basically a <em/read-only/ array of character pointers, that lists the <tt/NULL/ terminated list of environment variables set so far. <p> 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 <tt/pam_end()/ otherwise all the variable information will be lost. (There are functions in <tt/libpam_misc/ for this purpose: <tt/pam_misc_copy_env()/ and <tt/pam_misc_drop_env()/.) <sect1>Other functions provided by <tt/libpam/ <sect2>Understanding errors <p> <itemize> <item> <tt>extern const char *pam_strerror(pam_handle_t *pamh, int errnum);</tt> <p> This function returns some text describing the <bf/Linux-PAM/ error associated with the argument <tt/errnum/. If the error is not recognized <tt/``Unknown Linux-PAM error''/ is returned. </itemize> <sect2>Planning for delays <p> <itemize> <item> <tt>extern int pam_fail_delay(pam_handle_t *pamh, unsigned int micro_sec)</tt> <p> This function is offered by <bf/Linux-PAM/ to facilitate time delays following a failed call to <tt/pam_authenticate()/ and before control is returned to the application. When using this function the module programmer should check if it is available with, <tscreen> <verb> #ifdef HAVE_PAM_FAIL_DELAY .... #endif /* HAVE_PAM_FAIL_DELAY */ </verb> </tscreen> <p> Generally, an application requests that a user is authenticated by <bf/Linux-PAM/ through a call to <tt/pam_authenticate()/ or <tt/pam_chauthtok()/. These functions calls each of the <em/stacked/ authentication modules listed in the <tt>/etc/pam.conf</tt> file. As directed by this file, one of more of the modules may fail causing the <tt/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 <em/brute force/ dictionary attacks primarily, but also helps hinder <em/timed/ (covert channel) attacks. <p> The <tt/pam_fail_delay()/ function provides the mechanism by which an application or module can suggest a minimum delay (of <tt/micro_sec/ <em/micro-seconds/). <bf/Linux-PAM/ keeps a record of the longest time requested with this function. Should <tt/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. <p> Independent of success, the delay time is reset to its zero default value when <bf/Linux-PAM/ returns control to the application. </itemize> <sect>What is expected of a module <p> The module must supply a sub-set of the six functions listed below. Together they define the function of a <bf/Linux-PAM module/. Module developers are strongly urged to read the comments on security that follow this list. <sect1> Overview <p> The six module functions are grouped into four independent management groups. These groups are as follows: <em/authentication/, <em/account/, <em/session/ and <em/password/. To be properly defined, a module must define all functions within at least one of these groups. A single module may contain the necessary functions for <em/all/ four groups. <sect2> Functional independence <p> The independence of the four groups of service a module can offer means that the module should allow for the possibility that any one of these four services may legitimately be called in any order. Thus, the module writer should consider the appropriateness of performing a service without the prior success of some other part of the module. <p> As an informative example, consider the possibility that an application applies to change a user's authentication token, without having first requested that <bf/Linux-PAM/ authenticate the user. In some cases this may be deemed appropriate: when <tt/root/ wants to change the authentication token of some lesser user. In other cases it may not be appropriate: when <tt/joe/ maliciously wants to reset <tt/alice/'s password; or when anyone other than the user themself wishes to reset their <em/KERBEROS/ authentication token. A policy for this action should be defined by any reasonable authentication scheme, the module writer should consider this when implementing a given module. <sect2> Minimizing administration problems <p> To avoid system administration problems and the poor construction of a <tt>/etc/pam.conf</tt> file, the module developer may define all six of the following functions. For those functions that would not be called, the module should return <tt/PAM_SERVICE_ERR/ and write an appropriate message to the system log. When this action is deemed inappropriate, the function would simply return <tt/PAM_IGNORE/. <sect2> Arguments supplied to the module <p> The <tt/flags/ argument of each of the following functions can be logically OR'd with <tt/PAM_SILENT/, which is used to inform the module to not pass any <em/text/ (errors or warnings) to the application. <p> The <tt/argc/ and <tt/argv/ arguments are taken from the line appropriate to this module---that is, with the <em/service_name/ matching that of the application---in the configuration file (see the <bf/Linux-PAM/ System Administrators' Guide). Together these two parameters provide the number of arguments and an array of pointers to the individual argument tokens. This will be familiar to C programmers as the ubiquitous method of passing command arguments to the function <tt/main()/. Note, however, that the first argument (<tt/argv[0]/) is a true argument and <bf/not/ the name of the module. <sect1> Authentication management <p> To be correctly initialized, <tt/PAM_SM_AUTH/ must be <tt/#define/'d prior to including <tt><security/pam_modules.h></tt>. This will ensure that the prototypes for static modules are properly declared. <p> <itemize> <item> <tt>PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv);</tt> <p> This function performs the task of authenticating the user. <p> The <tt/flags/ argument can be a logically OR'd with <tt/PAM_SILENT/ and optionally take the following value: <p><descrip> <tag><tt/PAM_DISALLOW_NULL_AUTHTOK/</tag> return <tt/PAM_AUTH_ERR/ if the database of authentication tokens for this authentication mechanism has a <tt/NULL/ entry for the user. Without this flag, such a <tt/NULL/ token will lead to a success without the user being prompted. </descrip> <p> Besides <tt/PAM_SUCCESS/ return values that can be sent by this function are one of the following: <descrip> <tag><tt/PAM_AUTH_ERR/</tag> The user was not authenticated <tag><tt/PAM_CRED_INSUFFICIENT/</tag> For some reason the application does not have sufficient credentials to authenticate the user. <tag><tt/PAM_AUTHINFO_UNAVAIL/</tag> The modules were not able to access the authentication information. This might be due to a network or hardware failure etc. <tag><tt/PAM_USER_UNKNOWN/</tag> The supplied username is not known to the authentication service <tag><tt/PAM_MAXTRIES/</tag> One or more of the authentication modules has reached its limit of tries authenticating the user. Do not try again. </descrip> <item> <tt>PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv);</tt> <p> This function performs the task of altering the credentials of the user with respect to the corresponding authorization scheme. Generally, an authentication module may have access to more information about a user than their authentication token. This function is used to append such information to the application. It should only be called <em/after/ the user has been authenticated. <p> Permitted flags, one of which, may be logically OR'd with <tt/PAM_SILENT/ are, <p><descrip> <tag><tt/PAM_ESTABLISH_CRED/</tag> Set the credentials for the authentication service, <tag><tt/PAM_DELETE_CRED/</tag> Delete the credentials associated with the authentication service, <tag><tt/PAM_REINITIALIZE_CRED/</tag> Reinitialize the user credentials, and <tag><tt/PAM_REFRESH_CRED/</tag> Extend the lifetime of the user credentials. </descrip> <p> Besides <tt/PAM_SUCCESS/, the module may return one of the following errors: <p><descrip> <tag><tt/PAM_CRED_UNAVAIL/</tag> This module cannot retrieve the user's credentials. <tag><tt/PAM_CRED_EXPIRED/</tag> The user's credentials have expired. <tag><tt/PAM_USER_UNKNOWN/</tag> The user is not known to this authentication module. <tag><tt/PAM_CRED_ERR/</tag> This module was unable to set the credentials of the user. </descrip> </itemize> <sect1> Account management <p> To be correctly initialized, <tt/PAM_SM_ACCOUNT/ must be <tt/#define/'d prior to including <tt><security/pam_modules.h></tt>. This will ensure that the prototype for a static module is properly declared. <p> <itemize> <item> <tt>PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv);</tt> <p> This function performs the task of establishing whether the user is permitted to gain access at this time. It should be understood that the user has previously been validated by an authentication module. This function checks for other things. Such things might be: the time of day or the date, the terminal line, remote hostname, etc. . <p> This function may also determine things like the expiration on passwords, and respond that the user change it before continuing. <p> Valid flags, which may be logically OR'd with <tt/PAM_SILENT/, are the same as those applicable to the <tt/flags/ argument of <tt/pam_sm_authenticate/. <p> This function may return one of the following errors, <descrip> <tag><tt/PAM_ACCT_EXPIRED/</tag> The user is no longer permitted access to the system. <tag><tt/PAM_AUTH_ERR/</tag> There was an authentication error. <tag><tt/PAM_AUTHTOKEN_REQD/</tag> The user's authentication token has expired. Before calling this function again the application will arrange for a new one to be given. This will likely result in a call to <tt/pam_sm_chauthtok()/. <tag><tt/PAM_USER_UNKNOWN/</tag> The user is not known to the module's account management component. </descrip> </itemize> <sect1> Session management <p> To be correctly initialized, <tt/PAM_SM_SESSION/ must be <tt/#define/'d prior to including <tt><security/pam_modules.h></tt>. This will ensure that the prototypes for static modules are properly declared. <p> The following two functions are defined to handle the initialization/termination of a session. For example, at the beginning of a session the module may wish to log a message with the system regarding the user. Similarly, at the end of the session the module would inform the system that the user's session has ended. <p> It should be possible for sessions to be opened by one application and closed by another. This either requires that the module uses only information obtained from <tt/pam_get_item()/, or that information regarding the session is stored in some way by the operating system (in a file for example). <p> <itemize> <item> <tt>PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv);</tt> <p> This function is called to commence a session. The only valid, but optional, flag is <tt/PAM_SILENT/. <p> As a return value, <tt/PAM_SUCCESS/ signals success and <tt/PAM_SESSION_ERR/ failure. <item> <tt>PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv);</tt> <p> This function is called to terminate a session. The only valid, but optional, flag is <tt/PAM_SILENT/. <p> As a return value, <tt/PAM_SUCCESS/ signals success and <tt/PAM_SESSION_ERR/ failure. </itemize> <sect1> Password management <p> To be correctly initialized, <tt/PAM_SM_PASSWORD/ must be <tt/#define/'d prior to including <tt><security/pam_modules.h></tt>. This will ensure that the prototype for a static module is properly declared. <p> <itemize> <item> <tt>PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv);</tt> <p> This function is used to (re-)set the authentication token of the user. A valid flag, which may be logically OR'd with <tt/PAM_SILENT/, can be built from the following list, <descrip> <tag><tt/PAM_CHANGE_EXPIRED_AUTHTOK/</tag> This argument indicates to the module that the users authentication token (password) should only be changed if it has expired. This flag is optional and <em/must/ be combined with one of the following two flags. Note, however, the following two options are <em/mutually exclusive/. <tag><tt/PAM_PRELIM_CHECK/</tag> This indicates that the modules are being probed as to their ready status for altering the user's authentication token. If the module requires access to another system over some network it should attempt to verify it can connect to this system on receiving this flag. If a module cannot establish it is ready to update the user's authentication token it should return <tt/PAM_TRY_AGAIN/, this information will be passed back to the application. <tag><tt/PAM_UPDATE_AUTHTOK/</tag> This informs the module that this is the call it should change the authorization tokens. If the flag is logically OR'd with <tt/PAM_CHANGE_EXPIRED_AUTHTOK/, the token is only changed if it has actually expired. </descrip> <p> Note, the <bf/Linux-PAM/ library calls this function twice in succession. The first time with <tt/PAM_PRELIM_CHECK/ and then, if the module does not return <tt/PAM_TRY_AGAIN/, subsequently with <tt/PAM_UPDATE_AUTHTOK/. It is only on the second call that the authorization token is (possibly) changed. <p> <tt/PAM_SUCCESS/ is the only successful return value, valid error-returns are: <descrip> <tag><tt/PAM_AUTHTOK_ERR/</tag> The module was unable to obtain the new authentication token. <tag><tt/PAM_AUTHTOK_RECOVERY_ERR/</tag> The module was unable to obtain the old authentication token. <tag><tt/PAM_AUTHTOK_LOCK_BUSY/</tag> Cannot change the authentication token since it is currently locked. <tag><tt/PAM_AUTHTOK_DISABLE_AGING/</tag> Authentication token aging has been disabled. <tag><tt/PAM_PERM_DENIED/</tag> Permission denied. <tag><tt/PAM_TRY_AGAIN/</tag> Preliminary check was unsuccessful. Signals an immediate return to the application is desired. <tag><tt/PAM_USER_UNKNOWN/</tag> The user is not known to the authentication token changing service. </descrip> </itemize> <sect>Generic optional arguments <p> Here we list the generic arguments that all modules can expect to be passed. They are not mandatory, and their absence should be accepted without comment by the module. <p> <descrip> <tag><tt/debug/</tag> Use the <tt/syslog(3)/ call to log debugging information to the system log files. <tag><tt/no_warn/</tag> Instruct module to not give warning messages to the application. <tag><tt/use_first_pass/</tag> The module should not prompt the user for a password. Instead, it should obtain the previously typed password (by a call to <tt/pam_get_item()/ for the <tt/PAM_AUTHTOK/ item), and use that. If that doesn't work, then the user will not be authenticated. (This option is intended for <tt/auth/ and <tt/passwd/ modules only). <tag><tt/try_first_pass/</tag> The module should attempt authentication with the previously typed password (by a call to <tt/pam_get_item()/ for the <tt/PAM_AUTHTOK/ item). If that doesn't work, then the user is prompted for a password. (This option is intended for <tt/auth/ modules only). <tag><tt/use_mapped_pass/</tag> <bf/WARNING:/ coding this functionality may cause the module writer to break <em/local/ encryption laws. For example, in the U.S. there are restrictions on the export computer code that is capable of strong encryption. It has not been established whether this option is affected by this law, but one might reasonably assume that it does until told otherwise. For this reason, this option is not supported by any of the modules distributed with <bf/Linux-PAM/. The intended function of this argument, however, is that the module should take the existing authentication token from a previously invoked module and use it as a key to retrieve the authentication token for this module. For example, the module might create a strong hash of the <tt/PAM_AUTHTOK/ item (established by a previously executed module). Then, with logical-exclusive-or, use the result as a <em/key/ to safely store/retrieve the authentication token for this module in/from a local file <em/etc/. . </descrip> <sect>Programming notes <p> Here we collect some pointers for the module writer to bear in mind when writing/developing a <bf/Linux-PAM/ compatible module. <sect1>Security issues for module creation <sect2>Sufficient resources <p> Care should be taken to ensure that the proper execution of a module is not compromised by a lack of system resources. If a module is unable to open sufficient files to perform its task, it should fail gracefully, or request additional resources. Specifically, the quantities manipulated by the <tt/setrlimit(2)/ family of commands should be taken into consideration. <sect2>Who's who? <p> Generally, the module may wish to establish the identity of the user requesting a service. This may not be the same as the username returned by <tt/pam_get_user()/. Indeed, that is only going to be the name of the user under whose identity the service will be given. This is not necessarily the user that requests the service. <p> In other words, user X runs a program that is setuid-Y, it grants the user to have the permissions of Z. A specific example of this sort of service request is the <em/su/ program: user <tt/joe/ executes <em/su/ to become the user <em/jane/. In this situation X=<tt/joe/, Y=<tt/root/ and Z=<tt/jane/. Clearly, it is important that the module does not confuse these different users and grant an inappropriate level of privilege. <p> The following is the convention to be adhered to when juggling user-identities. <p> <itemize> <item>X, the identity of the user invoking the service request. This is the user identifier; returned by the function <tt/getuid(2)/. <item>Y, the privileged identity of the application used to grant the requested service. This is the <em/effective/ user identifier; returned by the function <tt/geteuid(2)/. <item>Z, the user under whose identity the service will be granted. This is the username returned by <tt/pam_get_user(2)/ and also stored in the <bf/Linux-PAM/ item, <tt/PAM_USER/. <item><bf/Linux-PAM/ has a place for an additional user identity that a module may care to make use of. This is the <tt/PAM_RUSER/ item. Generally, network sensitive modules/applications may wish to set/read this item to establish the identity of the user requesting a service from a remote location. </itemize> <p> Note, if a module wishes to modify the identity of either the <tt/uid/ or <tt/euid/ of the running process, it should take care to restore the original values prior to returning control to the <bf/Linux-PAM/ library. <sect2>Using the conversation function <p> Prior to calling the conversation function, the module should reset the contents of the pointer that will return the applications response. This is a good idea since the application may fail to fill the pointer and the module should be in a position to notice! <p> The module should be prepared for a failure from the conversation. The generic error would be <tt/PAM_CONV_ERR/, but anything other than <tt/PAM_SUCCESS/ should be treated as indicating failure. <sect2>Authentication tokens <p> To ensure that the authentication tokens are not left lying around the items, <tt/PAM_AUTHTOK/ and <tt/PAM_OLDAUTHTOK/, are not available to the application: they are defined in <tt><security/pam_modules.h></tt>. This is ostensibly for security reasons, but a maliciously programmed application will always have access to all memory of the process, so it is only superficially enforced. As a general rule the module should overwrite authentication tokens as soon as they are no longer needed. Especially before <tt/free()/'ing them. The <bf/Linux-PAM/ library is required to do this when either of these authentication token items are (re)set. <p> Not to dwell too little on this concern; should the module store the authentication tokens either as (automatic) function variables or using <tt/pam_[gs]et_data()/ the associated memory should be over-written explicitly before it is released. In the case of the latter storage mechanism, the associated <tt/cleanup()/ function should explicitly overwrite the <tt/*data/ before <tt/free()/'ing it: for example, <tscreen> <verb> /* * An example cleanup() function for releasing memory that was used to * store a password. */ int cleanup(pam_handle_t *pamh, void *data, int error_status) { char *xx; if ((xx = data)) { while (*xx) *xx++ = '\0'; free(data); } return PAM_SUCCESS; } </verb> </tscreen> <sect1>Use of <tt/syslog(3)/ <p> Only rarely should error information be directed to the user. Usually, this is to be limited to ``<em/sorry you cannot login now/'' type messages. Information concerning errors in the configuration file, <tt>/etc/pam.conf</tt>, or due to some system failure encountered by the module, should be written to <tt/syslog(3)/ with <em/facility-type/ <tt/LOG_AUTHPRIV/. <p> With a few exceptions, the level of logging is, at the discretion of the module developer. Here is the recommended usage of different logging levels: <p> <itemize> <item> As a general rule, errors encountered by a module should be logged at the <tt/LOG_ERR/ level. However, information regarding an unrecognized argument, passed to a module from an entry in the <tt>/etc/pam.conf</tt> file, is <bf/required/ to be logged at the <tt/LOG_ERR/ level. <item> Debugging information, as activated by the <tt/debug/ argument to the module in <tt>/etc/pam.conf</tt>, should be logged at the <tt/LOG_DEBUG/ level. <item> If a module discovers that its personal configuration file or some system file it uses for information is corrupted or somehow unusable, it should indicate this by logging messages at level, <tt/LOG_ALERT/. <item> Shortages of system resources, such as a failure to manipulate a file or <tt/malloc()/ failures should be logged at level <tt/LOG_CRIT/. <item> Authentication failures, associated with an incorrectly typed password should be logged at level, <tt/LOG_NOTICE/. </itemize> <sect1> Modules that require system libraries <p> Writing a module is much like writing an application. You have to provide the "conventional hooks" for it to work correctly, like <tt>pam_sm_authenticate()</tt> etc., which would correspond to the <tt/main()/ function in a normal function. <p> Typically, the author may want to link against some standard system libraries. As when one compiles a normal program, this can be done for modules too: you simply append the <tt>-l</tt><em>XXX</em> arguments for the desired libraries when you create the shared module object. To make sure a module is linked to the <tt>lib<em>whatever</em>.so</tt> library when it is <tt>dlopen()</tt>ed, try: <tscreen> <verb> % gcc -shared -Xlinker -x -o pam_module.so pam_module.o -lwhatever </verb> </tscreen> <sect1> Added requirements for <em/statically/ loaded modules. <!-- Copyright (C) Michael K. Johnson 1996. Last modified: AGM 1996/5/31. --> <p> Modules may be statically linked into libpam. This should be true of all the modules distributed with the basic <bf/Linux-PAM/ distribution. To be statically linked, a module needs to export information about the functions it contains in a manner that does not clash with other modules. The extra code necessary to build a static module should be delimited with <tt/#ifdef PAM_STATIC/ and <tt/#endif/. The static code should do the following: <itemize> <item> Define a single structure, <tt/struct pam_module/, called <tt>_pam_<it>modname</it>_modstruct</tt>, where <tt><it>modname</it></tt> is the name of the module <bf/as used in the filesystem/ but without the leading directory name (generally <tt>/usr/lib/security/</tt> or the suffix (generally <tt/.so/). </itemize> <p> As a simple example, consider the following module code which defines a module that can be compiled to be <em/static/ or <em/dynamic/: <p> <tscreen> <verb> #include <stdio.h> /* for NULL define */ #define PAM_SM_PASSWORD /* the only pam_sm_... function declared */ #include <security/pam_modules.h> PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) { return PAM_SUCCESS; } #ifdef PAM_STATIC /* for the case that this module is static */ struct pam_module _pam_modname_modstruct = { /* static module data */ "pam_modname", NULL, NULL, NULL, NULL, NULL, pam_sm_chauthtok, }; #endif /* end PAM_STATIC */ </verb> </tscreen> <p> To be linked with <em/libpam/, staticly-linked modules must be built from within the <tt>Linux-PAM-X.YY/modules/</tt> subdirectory of the <bf/Linux-PAM/ source directory as part of a normal build of the <bf/Linux-PAM/ system. The <em/Makefile/, for the module in question, must execute the <tt/register_static/ shell script that is located in the <tt>Linux-PAM-X.YY/modules/</tt> subdirectory. This is to ensure that the module is properly registered with <em/libpam/. The <bf/two/ manditory arguments to <tt/register_static/ are the title, and the pathname of the object file containing the module's code. The pathname is specified relative to the <tt>Linux-PAM-X.YY/modules</tt> directory. The pathname may be an empty string---this is for the case that a single object file needs to register more than one <tt/struct pam_module/. In such a case, exactly one call to <tt/register_static/ must indicate the object file. <p> Here is an example; a line in the <em/Makefile/ might look like this: <tscreen> <verb> register: ifdef STATIC (cd ..; ./register_static pam_modname pam_modname/pam_modname.o) endif </verb> </tscreen> For some further examples, see the <tt>modules</tt> subdirectory of the current <bf/Linux-PAM/ distribution. <p> <sect>An example module file <p> <em> perhaps this should point to a place in the file structure!? </em> <sect>Files <p><descrip> <tag><tt>/usr/lib/libpam.so.*</tt></tag> the shared library providing applications with access to <bf/Linux-PAM/. <tag><tt>/etc/pam.conf</tt></tag> the <bf/Linux-PAM/ configuration file. <tag><tt>/usr/lib/security/pam_*.so</tt></tag> the primary location for <bf/Linux-PAM/ dynamically loadable object files; the modules. </descrip> <sect>See also <p><itemize> <item>The <bf/Linux-PAM/ System Administrators' Guide. <item>The <bf/Linux-PAM/ Application Writers' Guide. <item> V. Samar and R. Schemers (SunSoft), ``UNIFIED LOGIN WITH PLUGGABLE AUTHENTICATION MODULES'', Open Software Foundation Request For Comments 86.0, October 1995. </itemize> <sect>Notes <p> I intend to put development comments here... like ``at the moment this isn't actually supported''. At release time what ever is in this section will be placed in the Bugs section below! :) <p> <itemize> <item> Perhaps we should keep a registry of data-names as used by <tt/pam_[gs]et_data()/ so there are no unintentional problems due to conflicts? <item> <tt/pam_strerror()/ should be internationalized.... <item> There has been some debate about whether <tt/initgroups()/ should be in an application or in a module. It was settled by Sun who stated that initgroups is an action of the <em/application/. The modules are permitted to add additional groups, however. <item> Refinements/futher suggestions to <tt/syslog(3)/ usage by modules are needed. </itemize> <sect>Author/acknowledgments <p> This document was written by Andrew G. Morgan (<tt/morgan@transmeta.com/) with many contributions from <!-- insert credits here --> <!-- an sgml list of people to credit for their contributions to Linux-PAM --> <!-- an sgml list of people to credit for their contributions to Linux-PAM $Id: CREDITS,v 1.4 1997/04/05 06:47:26 morgan Exp morgan $ --> Peter Allgeyer, Tim Baverstock, Craig S. Bell, Derrick J. Brashear, Ben Buxton, Oliver Crow, Chris Dent, Marc Ewing, Cristian Gafton, Eric Hester, Roger Hu, Eric Jacksch, Michael K. Johnson, David Kinchlea, Nicolai Langfeldt, Elliot Lee, Al Longyear, Ingo Luetkebohle, Marek Michalkiewicz, Aleph One, Martin Pool, Sean Reifschneider, Erik Troan, Theodore Ts'o, Jeff Uphoff, Myles Uyema, Savochkin Andrey Vladimirovich, Ronald Wahl, David Wood, John Wilmes, Joseph S. D. Yao and Alex O. Yuriev. <p> Thanks are also due to Sun Microsystems, especially to Vipin Samar and Charlie Lai for their advice. At an early stage in the development of <bf/Linux-PAM/, Sun graciously made the documentation for their implementation of PAM available. This act greatly accelerated the development of <bf/Linux-PAM/. <sect>Bugs/omissions <p> Few PAM modules currently exist. Few PAM-aware applications exist. This document is hopelessly unfinished. Only a partial list of people is credited for all the good work they have done. <sect>Copyright information for this document <p> Copyright (c) Andrew G. Morgan 1996, 1997. All rights reserved. <newline> Email: <tt><morgan@transmeta.com></tt> <p> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: <p> <itemize> <item> 1. Redistributions of source code must retain the above copyright notice, and the entire permission notice in its entirety, including the disclaimer of warranties. <item> 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. <item> 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. </itemize> <p> <bf/Alternatively/, this product may be distributed under the terms of the GNU General Public License (GPL), in which case the provisions of the GNU GPL are required <bf/instead of/ the above restrictions. (This clause is necessary due to a potential bad interaction between the GNU GPL and the restrictions contained in a BSD-style copyright.) <p> THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <p> <tt>$Id: pam_modules.sgml,v 1.19 1997/04/05 06:49:14 morgan Exp morgan $</tt> </article>