Section 28.2. Pluggable Authentication Modules


28.2. Pluggable Authentication Modules

The C library interface is fine for looking up user information, but does not allow the system administrator sufficient control over how authentication is performed.

Pluggable Authentication Modules, or PAM, is a specification and library specifically for configuring authentication on a system. The library provides a standard and relatively simple interface for authenticating users and for changing authentication information (such as a user's password). The Linux-PAM implementation[1] includes full documentation on how to program to the PAM interface, including documentation on how to write new PAM modules (The Module Writers' Manual) and how to write applications that make use of PAM (The Application Developers' Manual). Here, we merely show simple use of PAM for an application that wishes to check passwords appropriately.


PAM is a standard interface, specified by DCE, X/Open, and The Open Group. It is included as a part of several versions of Unix and practically all versions of Linux. It is a portable interface, so we recommend that you authenticate users using PAM. If you need to port code written to the PAM standard to an operating system that does not support PAM, the port will be trivial. However, because PAM is a somewhat rigid standard, it can be less easy to port applications that do not support PAM to a system that does use PAM.

Beyond authentication services (determining whether a user really is who he says he is, changing authentication information like passwords), PAM also does account management (determining whether this user is allowed to log in at this time and on this terminal), and credential management (typically, authentication tokens such as those used for X and Kerberos but expressly not uids and gids).

The Linux-PAM implementation provides both the standard libpam library and a set of nonstandard helper functions in the libpam_misc library. The main header file for writing PAM-aware applications is <security/pam_appl.h>.

We describe the core routines used to build an application that depends on PAM for its user authentication, and then provide a sample application, pamexample, that uses both the libpam library and the libpam_misc library.

28.2.1. PAM Conversations

PAM separates policy from mechanism; the PAM modules that implement the policy do not directly interact with the user, and the application does not set policy. All policy is established by the system administrator in a policy definition file, and is implemented by the modules invoked by the policy definition file. The conversation structure, struct pam_conv, tells modules how to ask the application to request information from the user.

 #include <security/pam_appl.h> struct pam_conv {     int (*conv)(int num_msg, const struct pam_message **msg,                 struct pam_response **resp, void *appdata_ptr);     void *appdata_ptr; }; 

The conv() member is a pointer to a conversation function that accepts messages to give to the user in struct pam_message and returns the user-supplied information in struct pam_response. The libpam_misc library provides a conversation function called misc_conv that works fine for text-based console applications. To use it (which we recommend that you do if possible), you need to include <security/pam_misc.h> and fill in the conv member with misc_conv. The "pamexample" program presented on page 641 uses this simple mechanism.

Alternatively, you will have to implement your own conversation function. To do so, you need to understand two more data structures:

 struct pam_message {     int msg_style;     const char *msg; }; struct pam_response {     char *resp;     int resp_retcode;    /* currently unused, zero expected */ }; 

The conversation function is passed an array of pointers to pam_message structures and an array of pointers to pam_response structures, each of length num_msg. Provide the response, if any, to each pam_message structure in the pam_response structure with the same array index. The msg_style can be one of the following:


Print text specified in msg as informational text (for example, on the standard output file descriptor), ask user for a response without echoing the characters typed (as for a password), and return the text in a newly allocated character string stored in the associated pam_response's resp.


Print text specified in msg as informational text (for example, on the standard output file descriptor), ask user for a response while echoing the characters typed (as for a user name), and return the text in a newly allocated character string stored in the associated pam_response's resp.


Print text specified in msg as error text (for example, on the standard error file descriptor), set the associated pam_response's resp to NULL.


Print text specified in msg as informational text (for example, on the standard output file descriptor), set the associated pam_response's resp to NULL.

Other values may be defined as extensions to the standard; your conversation function should ignore them if it is not written to handle them, and should merely provide a NULL response for them. PAM (more specifically, the PAM module making the request) is responsible for freeing every non-NULL resp string, as well as the pam_message and pam_response structure arrays.

The appdata_ptr member that is set in the conversation structure is passed to the conversation function. This is useful if you are using one conversation function in multiple contexts, or if you wish to pass context information into the conversation function. This information might include an X display specification, an internal data structure storing file descriptors for a connection, or any other data that your application finds useful. It is not interpreted by the PAM library in any way.

The conversation function should return PAM_CONV_ERR if an error is encountered during execution, and PAM_SUCCESS otherwise.

28.2.2. PAM Actions

PAM does not hold any static information in the library between calls, and instead stores all persistent information using an opaque data structure that is passed to all calls. This opaque object, pam_handle_t, is maintained by PAM but held by the calling application. It is initialized with the pam_start() function and deallocated by the pam_end() function:

 #include <security/pam_appl.h> int pam_start(const char *service_name, const char *user,               const struct pam_conv *pam_conversation,               pam_handle_t **pamh); int pam_end(pam_handle_t *pamh, int pam_status); 

The service_name argument is intended to be a unique name for your application. This unique name allows the system administrator to configure security specifically for your application; two applications using the same service_name necessarily share the same configuration. The user argument is the user name being authenticated. The pam_conversation argument is the conversation structure already discussed. The pamh argument is the opaque object that tracks internal state. pam_start() is demonstrated on line 97 of pamexample.c.

The pam_end() function, demonstrated on line 137 of pamexample.c, cleans up all state stored in the opaque object pamh, and informs the modules referenced by it of the final status of the actions; if the application was successful in its use of PAM, it should provide a pam_status of PAM_SUCCESS; otherwise, it should provide the most recent error returned by PAM.

There are cases in which PAM modules can make use of extra information when deciding whether to authenticate a user; information provided by the system and not by the user. In addition, there are times that PAM modules may need to alert the application of a change. The mechanism for passing this extra information around is the PAM item. An item's value is set with the pam_set_item() function, and queried with the pam_get_item() function:

 #include <security/pam_appl.h> extern int pam_set_item(pam_handle_t *pamh, int item_type,                         const void *item); extern int pam_get_item(const pam_handle_t *pamh, int item_type,                         const void **item); 

The item_type argument determines the identity and semantics of the item. We cover only the commonly used values of item_type here:


item is a pointer to a string containing the name of the TTY device that the authentication request is associated with. This might be ttyS0 for the first serial port on a standard system, or pts/0 for the first pseudo-tty, or tty1 for the first virtual console.


This is automatically set by pam_start() to the user argument passed to pam_start(). It is important to note that this name may change! If your application cares about the user name, it must check the value via pam_get_item() after calling into the PAM stack and before making use of the name in other code.


For network protocols (such as rsh and ssh), this item should be used to provide the remote system user name to any PAM modules that use it. This allows the system administrator to determine whether rhost-style authentication is allowed.


Like PAM_RUSER, PAM_RHOST should be set for network protocols where the name of the remote host might be used as a component of authentication or account management.

The rest of the functions each take two arguments, the pamh opaque object and an integer that can either be zero or the flag PAM_SILENT. The PAM_SILENT flag requests that the PAM framework and modules not generate informational messages, but does not prevent prompting for passwords. Normal applications do not set PAM_SILENT.

The pam_authenticate() function, demonstrated on line 100 of pamexample.c, does whatever the system administrator has configured this application (as determined by the service_name argument to the pam_start() function) to do to authenticate the user. This might include asking for one or more passwords, checking that the current user name (as determined by the PAM_USER PAM item, not by the current uid; PAM modules do not consider uid because applications calling PAM are normally explicitly not running as the user being authenticated) is the current user of the console, checking that the current user (again, by name) has authenticated to an equivalent level of service recently, checking the PAM items PAM_RUSER and PAM_RHOST against local tables of equivalent remote users and hosts (for example, as the rsh daemon does), or something else. (Note that most systems use the "shadow password" system, in which case, in order to protect the passwords, only processes with root permissions may check arbitrary users' passwords; a process not running as root may check only that uid's own password. This is one of the only exceptions to the rule that PAM modules must not consider the uid.)

The pam_acct_mgmt() function, demonstrated on line 107, assumes that pam_authenticate() has already been called and has succeeded, and then checks to see whether the user (again, specified by name) is permitted to do the requested access. It may consider the PAM items PAM_USER, PAM_TTY, PAM_RUSER, and PAM_RHOST in determining whether the access is allowed. For example, one particular user may be allowed on certain ttys only during certain hours, or each user in some class may be allowed only a certain number of concurrent logins, or certain sets of remote users and hosts may be allowed only during certain hours.

The pam_setcred() function, demonstrated on line 118, assumes that authentication has succeeded, and then sets up credentials for the user. While the uid, gid, and supplemental groups are technically credentials, they are not managed by pam_setcred() because to do so would not fit the security model of most applications. Instead, it sets up additional credentials. A likely candidate for a credential is a Kerberos ticket a file containing cryptographic data that gives the user permission to access certain resources.

The pam_open_session() function, demonstrated on line 113, opens a new session. If the process forks, pam_open_session() should be called after the fork because it may take actions such as setting rlimits (see pages 120-120), and if your process starts as the root user and then changes to the uid for the authenticated user, pam_open_session() should be called before dropping root privileges, because session modules may attempt system operations (such as mounting home directories) that are reserved for root.

The pam_close_session() function, demonstrated on line 128, closes the existing session. It can be called from a different process than the one that called pam_open_session(), as long as the same data is available to PAM the same arguments given to PAM, the same PAM items used for opening the session. Note that because the PAM_USER item can be changed during authentication and account management, you need to make sure that you have taken into account any changes made to PAM_USER if you call it from a separate process than the one that set up the session. The pam_close_session() function may require root privileges to function:

   1: /* pamexample.c */   2:   3: /* The pamexample program demonstrates some simple PAM handling.   4:  * You will either have to use the --service command line option   5:  * to choose a service name that is already installed ("system-auth"   6:  * may work, check for /etc/pam.d/system-auth on your system), or   7:  * install a system file * /etc/pam.d/pamexample with the following   8:  * four lines (ignoring leading "*" characters):   9:  * #%PAM-1.0  10:  * auth         required    /lib/security/  11:  * account      required    /lib/security/  12:  * session      required    /lib/security/  13:  *  14:  * Note that if you run this program as a non-root user, there  15:  * may be system limitations on what you can do; account management  16:  * may fail, you may not be able to test other users' passwords,  17:  * and session management may fail, all depending on how the  18:  * service is configured.  19:  */  20:  21: #include <security/pam_appl.h>  22: #include <security/pam_misc.h>  23: #include <popt.h>  24: #include <pwd.h>  25: #include <sys/types.h>  26: #include <stdio.h>  27: #include <stdlib.h>  28: #include <unistd.h>  29:  30: /* This struct can be an automatic, but it must not go out  31:  * of scope between pam_start() and pam_end(), so it is  32:  * easier in simple programs to make it a static instead  33:  */  34: static struct pam_conv my_conv = {  35:     misc_conv, /* use TTY conversation function from libpam_misc */  36:     NULL       /* we have no special data to pass to misc_conf */  37: };  38:  39: void check_success(pam_handle_t *pamh, int return_code) {  40:     if (return_code != PAM_SUCCESS) {  41:         fprintf(stderr, "%s\n", pam_strerror(pamh, return_code));  42:         exit(1);  43:     }  44: }  45:  46: int main(int argc, const char **argv) {  47:     pam_handle_t *pamh;  48:     struct passwd *pw;  49:     char *username=NULL, *service=NULL;  50:     int account = 1, session = 0;  51:     int c;  52:     poptContext optCon;  53:     struct poptOption optionsTable[] = {  54:         { "username", 'u', POPT_ARG_STRING, &username, 0,  55:           "Name of user to authenticate", "<username>" },  56:         { "service", 'S', POPT_ARG_STRING, &service, 0,  57:           "Name of service to initialize as (pamsample)",  58:           "<service>" },  59:         { "account", 'a', POPT_ARG_NONE|POPT_ARGFLAG_XOR,  60:           &account, 0,  61:           "toggle whether to do account management (on)", "" },  62:         { "session", 's', POPT_ARG_NONE|POPT_ARGFLAG_XOR,  63:           &session, 0,  64:           "toggle whether to start a session (off)", "" },  65:         POPT_AUTOHELP  66:         POPT_TABLEEND  67:     };  68:  69:     optCon = poptGetContext("pamexample", argc, argv,  70:                             optionsTable, 0);  71:     if ((c = poptGetNextOpt(optCon)) < -1) {  72:         fprintf(stderr, "%s: %s\n",  73:             poptBadOption(optCon, POPT_BADOPTION_NOALIAS),  74:             poptStrerror(c));  75:         return 1;  76:     }  77:     poptFreeContext(optCon);  78:  79:     if (!service) {  80:         /* Note that a normal application must not give this  81:          * option to the user; it exists here to make it  82:          * possible to test this application without making  83:          * system changes requiring root access.  84:          */  85:         service = "pamexample";  86:     }  87:  88:     if (!username) {  89:         /* default to current user */  90:         if (!(pw = getpwuid(getuid()))) {  91:             fprintf(stderr, "Username does not exist");  92:             exit(1);  93:         }  94:         username = strdup(pw->pw_name);  95:     }  96:  97:     c = pam_start(service, username, &my_conv, &pamh);  98:     check_success(pamh, c);  99: 100:     c = pam_authenticate(pamh, 0); 101:     check_success(pamh, c); 102: 103:     if (account) { 104:         /* if authentication did not succeed, account management 105:          * is not defined 106:          */ 107:         c = pam_acct_mgmt(pamh, 0); 108:         check_success(pamh, c); 109:     } 110: 111:     if (session) { 112:         /* We would fork here if we were going to fork */ 113:         c = pam_open_session(pamh, 0); 114:         check_success(pamh, c); 115: 116:         /* Note that this will not set uid, gid, or supplemental 117:            groups */ 118:         c = pam_setcred(pamh, 0); 119: 120:         /* We would drop permissions here if we were going to */ 121: 122:         /* Call a shell that has been "authenticated" */ 123:         printf("Running shell...\n"); 124:         system("exec bash -"); 125: 126:         /* We would wait4() here if we had forked instead of 127:            calling system() */ 128:         c = pam_close_session(pamh, 0); 129:         check_success(pamh, c); 130:     } 131: 132:     /* Real applications would report failure instead of 133:      * bailing out as we do in check_success at each stage, 134:      * so c might be something other than PAM_SUCCESS in 135:      * those cases. 136:      */ 137:     c = pam_end(pamh, c); 138:     check_success(pamh, c); 139: 140:     return 0; 141: } 


    Linux Application Development
    Linux Application Development (paperback) (2nd Edition)
    ISBN: 0321563220
    EAN: 2147483647
    Year: 2003
    Pages: 168 © 2008-2017.
    If you may any questions please contact us: