Sophie

Sophie

distrib > Mandriva > 8.1 > i586 > by-pkgid > a46cbe42e0ff9f3a2a3ed9d4555310d0 > files > 39

pam-doc-0.75-7mdk.i586.rpm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
<HEAD>
 <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
 <TITLE>The Linux-PAM Module Writers' Guide: Programming notes</TITLE>
 <LINK HREF="pam_modules-6.html" REL=next>
 <LINK HREF="pam_modules-4.html" REL=previous>
 <LINK HREF="pam_modules.html#toc5" REL=contents>
</HEAD>
<BODY>
<A HREF="pam_modules-6.html">Next</A>
<A HREF="pam_modules-4.html">Previous</A>
<A HREF="pam_modules.html#toc5">Contents</A>
<HR>
<H2><A NAME="s5">5. Programming notes</A></H2>

<P>Here we collect some pointers for the module writer to bear in mind
when writing/developing a <B>Linux-PAM</B> compatible module.
<P>
<H2><A NAME="ss5.1">5.1 Security issues for module creation</A>
</H2>

<H3>Sufficient resources</H3>

<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 <CODE>setrlimit(2)</CODE> family of commands
should be taken into consideration.
<P>
<H3>Who's who?</H3>

<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 <CODE>pam_get_user()</CODE>. 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>
<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</EM> program: user <CODE>joe</CODE> executes
<EM>su</EM> to become the user <EM>jane</EM>.  In this situation X=<CODE>joe</CODE>,
Y=<CODE>root</CODE> and Z=<CODE>jane</CODE>.  Clearly, it is important that the module
does not confuse these different users and grant an inappropriate
level of privilege.
<P>
<P>The following is the convention to be adhered to when juggling
user-identities.
<P>
<P>
<UL>
<LI>X, the identity of the user invoking the service request.
This is the user identifier; returned by the function <CODE>getuid(2)</CODE>.
</LI>
<LI>Y, the privileged identity of the application used to grant the
requested service.  This is the <EM>effective</EM> user identifier;
returned by the function <CODE>geteuid(2)</CODE>.
</LI>
<LI>Z, the user under whose identity the service will be granted.
This is the username returned by <CODE>pam_get_user(2)</CODE> and also stored
in the <B>Linux-PAM</B> item, <CODE>PAM_USER</CODE>.
</LI>
<LI><B>Linux-PAM</B> has a place for an additional user identity that
a module may care to make use of. This is the <CODE>PAM_RUSER</CODE> 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.
</LI>
</UL>
<P>
<P>Note, if a module wishes to modify the identity of either the <CODE>uid</CODE>
or <CODE>euid</CODE> of the running process, it should take care to restore
the original values prior to returning control to the <B>Linux-PAM</B>
library.
<P>
<H3>Using the conversation function</H3>

<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>
<P>The module should be prepared for a failure from the conversation. The
generic error would be <CODE>PAM_CONV_ERR</CODE>, but anything other than
<CODE>PAM_SUCCESS</CODE> should be treated as indicating failure.
<P>
<H3>Authentication tokens</H3>

<P>To ensure that the authentication tokens are not left lying around the
items, <CODE>PAM_AUTHTOK</CODE> and <CODE>PAM_OLDAUTHTOK</CODE>, are not available to
the application: they are defined in
<CODE>&lt;security/pam_modules.h&gt;</CODE>. 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 <CODE>free()</CODE>'ing them. The <B>Linux-PAM</B> library is
required to do this when either of these authentication token items
are (re)set.
<P>
<P>Not to dwell too little on this concern; should the module store the
authentication tokens either as (automatic) function variables or
using <CODE>pam_[gs]et_data()</CODE> the associated memory should be
over-written explicitly before it is released. In the case of the
latter storage mechanism, the associated <CODE>cleanup()</CODE> function
should explicitly overwrite the <CODE>*data</CODE> before <CODE>free()</CODE>'ing it:
for example,
<P>
<BLOCKQUOTE><CODE>
<PRE>
/*
 * 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;
}
</PRE>
</CODE></BLOCKQUOTE>
<P>
<H2><A NAME="ss5.2">5.2 Use of <CODE>syslog(3)</CODE></A>
</H2>

<P>Only rarely should error information be directed to the user. Usually,
this is to be limited to ``<EM>sorry you cannot login now</EM>'' type
messages. Information concerning errors in the configuration file,
<CODE>/etc/pam.conf</CODE>, or due to some system failure encountered by
the module, should be written to <CODE>syslog(3)</CODE> with
<EM>facility-type</EM> <CODE>LOG_AUTHPRIV</CODE>.
<P>
<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>
<P>
<UL>
<LI>As a general rule, errors encountered by a module should be logged at
the <CODE>LOG_ERR</CODE> level. However, information regarding an unrecognized
argument, passed to a module from an entry in the
<CODE>/etc/pam.conf</CODE> file, is <B>required</B> to be logged at the
<CODE>LOG_ERR</CODE> level.
</LI>
<LI>Debugging information, as activated by the <CODE>debug</CODE> argument to the
module in <CODE>/etc/pam.conf</CODE>, should be logged at the
<CODE>LOG_DEBUG</CODE> level.
</LI>
<LI>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, <CODE>LOG_ALERT</CODE>.
</LI>
<LI>Shortages of system resources, such as a failure to manipulate a file
or <CODE>malloc()</CODE> failures should be logged at level <CODE>LOG_CRIT</CODE>.
</LI>
<LI>Authentication failures, associated with an incorrectly typed password
should be logged at level, <CODE>LOG_NOTICE</CODE>.
</LI>
</UL>
<P>
<H2><A NAME="ss5.3">5.3 Modules that require system libraries</A>
</H2>

<P>Writing a module is much like writing an application. You have to
provide the "conventional hooks" for it to work correctly, like
<CODE>pam_sm_authenticate()</CODE> etc., which would correspond to the
<CODE>main()</CODE> function in a normal function.
<P>
<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 <CODE>-l</CODE><EM>XXX</EM> arguments
for the desired libraries when you create the shared module object. To
make sure a module is linked to the <CODE>lib<EM>whatever</EM>.so</CODE>
library when it is <CODE>dlopen()</CODE>ed, try:
<BLOCKQUOTE><CODE>
<PRE>
% gcc -shared -Xlinker -x -o pam_module.so pam_module.o -lwhatever
</PRE>
</CODE></BLOCKQUOTE>
<P>
<H2><A NAME="ss5.4">5.4 Added requirements for <EM>statically</EM> loaded modules.</A>
</H2>

<P>Modules may be statically linked into libpam. This should be true of
all the modules distributed with the basic <B>Linux-PAM</B>
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.
<P>The extra code necessary to build a static module should be delimited
with <CODE>#ifdef PAM_STATIC</CODE> and <CODE>#endif</CODE>. The static code should do
the following:
<UL>
<LI> Define a single structure, <CODE>struct pam_module</CODE>, called
<CODE>_pam_<I>modname</I>_modstruct</CODE>, where
<CODE><I>modname</I></CODE> is the name of the module <B>as used in the
filesystem</B> but without the leading directory name (generally
<CODE>/usr/lib/security/</CODE> or the suffix (generally <CODE>.so</CODE>).
</LI>
</UL>
<P>
<P>As a simple example, consider the following module code which defines
a module that can be compiled to be <EM>static</EM> or <EM>dynamic</EM>:
<P>
<P>
<BLOCKQUOTE><CODE>
<PRE>
#include &lt;stdio.h>                                    /* for NULL define */

#define PAM_SM_PASSWORD         /* the only pam_sm_... function declared */
#include &lt;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 */
</PRE>
</CODE></BLOCKQUOTE>
<P>
<P>To be linked with <EM>libpam</EM>, staticly-linked modules must be built
from within the <CODE>Linux-PAM-X.YY/modules/</CODE> subdirectory of the
<B>Linux-PAM</B> source directory as part of a normal build of the
<B>Linux-PAM</B> system.
<P>The <EM>Makefile</EM>, for the module in question, must execute the
<CODE>register_static</CODE> shell script that is located in the
<CODE>Linux-PAM-X.YY/modules/</CODE> subdirectory. This is to ensure that
the module is properly registered with <EM>libpam</EM>.
<P>The <B>two</B> manditory arguments to <CODE>register_static</CODE> are the
title, and the pathname of the object file containing the module's
code. The pathname is specified relative to the
<CODE>Linux-PAM-X.YY/modules</CODE> directory. The pathname may be an
empty string---this is for the case that a single object file needs to
register more than one <CODE>struct pam_module</CODE>. In such a case, exactly
one call to <CODE>register_static</CODE> must indicate the object file.
<P>
<P>Here is an example; a line in the <EM>Makefile</EM> might look like this:
<BLOCKQUOTE><CODE>
<PRE>
register:
ifdef STATIC
        (cd ..; ./register_static pam_modname pam_modname/pam_modname.o)
endif
</PRE>
</CODE></BLOCKQUOTE>
<P>For some further examples, see the <CODE>modules</CODE> subdirectory of
the current <B>Linux-PAM</B> distribution.
<P>
<HR>
<A HREF="pam_modules-6.html">Next</A>
<A HREF="pam_modules-4.html">Previous</A>
<A HREF="pam_modules.html#toc5">Contents</A>
</BODY>
</HTML>