<!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><security/pam_modules.h></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 <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 */ </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>