Developer's Guide to Oracle® Solaris 11 Security

Exit Print View

Updated: July 2014

A Simple PAM Consumer Example

The following PAM consumer application is provided as an example. The example is a basic terminal-lock application that validates a user trying to access a terminal.

    The example goes through the following steps:

  1. Initialize the PAM session.

    PAM sessions are initiated by calling the pam_start(3PAM) function. A PAM consumer application must first establish a PAM session before calling any of the other PAM functions.

      The pam_start(3PAM) function takes the following arguments:

    • plock – Service name, that is, the name of the application. The service name is used by the PAM framework to determine which rules in the configuration file, /etc/pam.conf or, the /etc/pam.d, are applicable. The service name is generally used for logging and error-reporting.

    • pw->pw_name – The username is the name of the user that the PAM framework acts on.

    • &conv – The conversation function, conv, which provides a generic means for PAM to communicate with a user or application. Conversation functions are necessary because the PAM modules have no way of knowing how communication is to be conducted. Communication can be by means of GUIs, the command line, a smart card reader, or other devices. For more information, see Writing Conversation Functions.

    • &pamh – The PAM handle, pamh, which is an opaque handle that is used by the PAM framework to store information about the current operation. This handle is returned by a successful call to pam_start().

    Note - An application that calls PAM interfaces must be sufficiently privileged to perform any needed operations such as authentication, password change, process credential manipulation, or audit state initialization. In this example, the application must be able to read /etc/shadow to verify the passwords for local users.
  2. Authenticate the user.

    The application calls pam_authenticate(3PAM) to authenticate the current user. Generally, the user is required to enter a password or other authentication token depending on the type of authentication service.

    The PAM framework invokes the modules configured for the service name plock which corresponds to the service module type of authentication, auth, in /etc/pam.conf, or, in the case of Oracle Solaris 11.1 OS, in /etc/pam.d/plock. If there are no auth entries for the plock service in either /etc/pam.conf or /etc/pam.d/plock, then auth entries for the other service are searched in /etc/pam.conf and finally in the /etc/pam.d/other file.

  3. Check account validity.

    The example uses the pam_acct_mgmt(3PAM) function to check the validity of the authenticated user's account. In this example, pam_acct_mgmt() checks for expiration of the password.

    The pam_acct_mgmt() function also uses the PAM_DISALLOW_NULL_AUTHTOK flag. If pam_acct_mgmt() returns PAM_NEW_AUTHTOK_REQD, then pam_chauthtok(3PAM) should be called to allow the authenticated user to change the password.

  4. Force the user to change passwords if the system discovers that the password has expired.

    The example uses a loop to call pam_chauthtok() until success is returned. The pam_chauthtok() function returns success if the user successfully changes his or her authentication information, which is usually the password. In this example, the loop continues until success is returned. More commonly, an application would set a maximum number of tries before terminating.

  5. Call pam_setcred(3PAM).

    The pam_setcred(3PAM) function is used to establish, modify, or delete user credentials. pam_setcred() is typically called when a user has been authenticated. The call is made after the account has been validated, but before a session has been opened. The pam_setcred() function is used with the PAM_ESTABLISH_CRED flag to establish a new user session. If the session is the renewal of an existing session, such as for lockscreen, pam_setcred() with the PAM_REFRESH_CRED flag should be called. If the session is changing the credentials, such as using su or assuming a role, then pam_setcred() with the PAM_REINITIALIZE_CRED flag should be called.

  6. Close the PAM session.

    The PAM session is closed by calling the pam_end(3PAM) function. pam_end() frees all PAM resources as well.

The following example shows the source code for the sample PAM consumer application.

Note - The source code for this example is also available through the Oracle download center. See
Example 3-1  Sample PAM Consumer Application
* Copyright (c) 2005, 201

2, Oracle and/or its affiliates. All rights reserved.


#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <signal.h>
#include <pwd.h>
#include <errno.h>
#include <security/pam_appl.h>

extern int pam_tty_conv(int num_msg, struct pam_message **msg,
	     struct pam_response **response, void *appdata_ptr);

/* Disable keyboard interrupts (Ctrl-C, Ctrl-Z, Ctrl-\) */
static void
	(void) signal(SIGINT, SIG_IGN);
	(void) signal(SIGTSTP, SIG_IGN);
	(void) signal(SIGQUIT, SIG_IGN);

/* Terminate current user session, i.e., logout */
static void
	pid_t pgroup = getpgrp();

	(void) signal(SIGTERM, SIG_IGN);
	(void) fprintf(stderr, "Sorry, your session can't be restored.\n");
	(void) fprintf(stderr, "Press return to terminate this session.\n");
	(void) getchar();
	(void) kill(-pgroup, SIGTERM);
	(void) sleep(2);
	(void) kill(-pgroup, SIGKILL);

main(int argc, char *argv)
	struct pam_conv conv = { pam_tty_conv, NULL };
	pam_handle_t *pamh;
	struct passwd *pw;
	int err;

	if ((pw = getpwuid(getuid())) == NULL) {
		(void) fprintf(stderr, "plock: Can't get username: %s\n",
	/* Initialize PAM framework */
	err = pam_start("plock", pw->pw_name, &conv, &pamh);
	if (err != PAM_SUCCESS) {
		(void) fprintf(stderr, "plock: pam_start failed: %s\n",
		    pam_strerror(pamh, err));

	/* Authenticate user in order to unlock screen */
	do {
		(void) fprintf(stderr, "Terminal locked for %s. ", pw->pw_name);
		err = pam_authenticate(pamh, 0);
		if (err == PAM_USER_UNKNOWN) {
		} else if (err != PAM_SUCCESS) {
			(void) fprintf(stderr, "Invalid password.\n");
	} while (err != PAM_SUCCESS);

	/* Make sure account and password are still valid */
	switch (err = pam_acct_mgmt(pamh, 0)) {
		/* User not allowed in anymore */
		/* The user's password has expired. Get a new one */
		do {
			err = pam_chauthtok(pamh, 0);
		} while (err == PAM_AUTHTOK_ERR);
		if (err != PAM_SUCCESS)

if (pam_setcred(pamh, PAM_REFRESH_CRED) != PAM_SUCCESS){

	(void) pam_end(pamh, 0);