 
 
 
 
 
pam_modules-6.htmlNext pam_modules-4.htmlPrevious pam_modules.html#toc5Contents 5. Programming notes 
Here we collect some pointers for the module writer to bear in mind
when writing/developing a 
Linux-PAM compatible module.
5.1 Security issues for module creation 
Sufficient resources
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 
setrlimit(2) family of commands
should be taken into consideration.
Who's who?
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 
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.
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 
su program: user joe executes
su to become the user jane.  In this situation X=joe,
Y=
root and Z=jane.  Clearly, it is important that the module
does not confuse these different users and grant an inappropriate
level of privilege.
The following is the convention to be adhered to when juggling
user-identities.
X, the identity of the user invoking the service request.
This is the user identifier; returned by the function 
getuid(2).
Y, the privileged identity of the application used to grant the
requested service.  This is the 
effective user identifier;
returned by the function 
geteuid(2).
Z, the user under whose identity the service will be granted.
This is the username returned by 
pam_get_user(2) and also stored
in the 
Linux-PAM item, PAM_USER.
Linux-PAM has a place for an additional user identity that
a module may care to make use of. This is the 
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.
Note, if a module wishes to modify the identity of either the uidor 
euid of the running process, it should take care to restore
the original values prior to returning control to the 
Linux-PAMlibrary.
Using the conversation function
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!
The module should be prepared for a failure from the conversation. The
generic error would be 
PAM_CONV_ERR, but anything other than
PAM_SUCCESS should be treated as indicating failure.
Authentication tokens
To ensure that the authentication tokens are not left lying around the
items, 
PAM_AUTHTOK and PAM_OLDAUTHTOK, are not available to
the application: they are defined in
<security/pam_modules.h>. 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 
free()'ing them. The Linux-PAM library is
required to do this when either of these authentication token items
are (re)set.
Not to dwell too little on this concern; should the module store the
authentication tokens either as (automatic) function variables or
using 
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 
cleanup() function
should explicitly overwrite the 
*data before free()'ing it:
for example,
/*
 * 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;
}
5.2 Use of syslog(3) 
Only rarely should error information be directed to the user. Usually,
this is to be limited to ``
sorry you cannot login now'' type
messages. Information concerning errors in the configuration file,
/etc/pam.conf, or due to some system failure encountered by
the module, should be written to 
syslog(3) with
facility-type LOG_AUTHPRIV.
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:
As a general rule, errors encountered by a module should be logged at
the 
LOG_ERR level. However, information regarding an unrecognized
argument, passed to a module from an entry in the
/etc/pam.conf file, is required to be logged at the
LOG_ERR level.
Debugging information, as activated by the debug argument to the
module in 
/etc/pam.conf, should be logged at the
LOG_DEBUG level.
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, 
LOG_ALERT.
Shortages of system resources, such as a failure to manipulate a file
or 
malloc() failures should be logged at level LOG_CRIT.
Authentication failures, associated with an incorrectly typed password
should be logged at level, 
LOG_NOTICE.
5.3 Modules that require system libraries 
Writing a module is much like writing an application. You have to
provide the "conventional hooks" for it to work correctly, like
pam_sm_authenticate() etc., which would correspond to the
main() function in a normal function.
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 
-lXXX arguments
for the desired libraries when you create the shared module object. To
make sure a module is linked to the 
libwhatever.solibrary when it is 
dlopen()ed, try:
% gcc -shared -Xlinker -x -o pam_module.so pam_module.o -lwhatever
5.4 Added requirements for statically loaded modules. 
Modules may be statically linked into libpam. This should be true of
all the modules distributed with the basic 
Linux-PAMdistribution.  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 
#ifdef PAM_STATIC and #endif. The static code should do
the following:
 Define a single structure, struct pam_module, called
_pam_modname_modstruct, where
modname is the name of the module as used in the
filesystem
 but without the leading directory name (generally
/usr/lib/security/ or the suffix (generally .so).
As a simple example, consider the following module code which defines
a module that can be compiled to be 
static or dynamic:
#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 */
To be linked with libpam, staticly-linked modules must be built
from within the 
Linux-PAM-X.YY/modules/ subdirectory of the
Linux-PAM source directory as part of a normal build of the
Linux-PAM system.
The Makefile, for the module in question, must execute the
register_static shell script that is located in the
Linux-PAM-X.YY/modules/ subdirectory. This is to ensure that
the module is properly registered with 
libpam.
The two manditory arguments to register_static are the
title, and the pathname of the object file containing the module's
code. The pathname is specified relative to the
Linux-PAM-X.YY/modules directory. The pathname may be an
empty string---this is for the case that a single object file needs to
register more than one 
struct pam_module. In such a case, exactly
one call to 
register_static must indicate the object file.
Here is an example; a line in the Makefile might look like this:
register:
ifdef STATIC
        (cd ..; ./register_static pam_modname pam_modname/pam_modname.o)
endif
For some further examples, see the modules subdirectory of
the current 
Linux-PAM distribution.
pam_modules-6.htmlNext pam_modules-4.htmlPrevious pam_modules.html#toc5Contents 