Skip Navigation Links | |
Exit Print View | |
![]() |
Developer's Guide to Oracle Solaris Security Oracle Solaris 10 8/11 Information Library |
1. Oracle Solaris Security for Developers (Overview)
2. Developing Privileged Applications
3. Writing PAM Applications and Services
Introduction to the PAM Framework
Requirements for PAM Consumers
Writing Conversation Functions
Writing Modules That Provide PAM Services
Requirements for PAM Service Providers
Sample PAM Provider Service Module
4. Writing Applications That Use GSS-API
7. Writing Applications That Use SASL
8. Introduction to the Oracle Solaris Cryptographic Framework
9. Writing User-Level Cryptographic Applications and Providers
10. Using the Smart Card Framework
A. Sample C-Based GSS-API Programs
D. Source Code for SASL Example
This section provides a sample application that uses several PAM functions.
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:
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, 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.
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 listed for the authentication service, auth, in /etc/pam.conf. The service name plock is used to determine which pam.conf entries are to be used. If there are no entries for plock, then the entries in other are used by default. If NULL passwords are explicitly disallowed in the application configuration file, then the PAM_DISALLOW_NULL_AUTHTOK flag should be passed. Solaris applications check the PASSREQ=YES setting in /etc/default/login.
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.
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.
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.
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 download center. See https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_SMI-Site/en_US/-/USD/ViewProductDetail-Start?ProductRef=Security_code-Dev1.1-G-F@CDS-CDS_SMI.
Example 3-1 Sample PAM Consumer Application
/* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #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 disable_kbd_signals(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 logout() { 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); exit(-1); } int /*ARGSUSED*/ main(int argc, char *argv) { struct pam_conv conv = { pam_tty_conv, NULL }; pam_handle_t *pamh; struct passwd *pw; int err; disable_kbd_signals(); if ((pw = getpwuid(getuid())) == NULL) { (void) fprintf(stderr, "plock: Can't get username: %s\n", strerror(errno)); exit(1); } /* 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)); exit(1); } /* 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) { logout(); } 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)) { case PAM_SUCCESS: break; case PAM_USER_UNKNOWN: case PAM_ACCT_EXPIRED: /* User not allowed in anymore */ logout(); break; case PAM_NEW_AUTHTOK_REQD: /* 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) logout(); break; default: logout(); } if (pam_setcred(pamh, PAM_REFRESH_CRED) != PAM_SUCCESS){ logout(); } (void) pam_end(pamh, 0); return(0); /*NOTREACHED*/ }
The previous example, Example 3-1, is a simple application that demonstrates only a few of the major PAM functions. This section describes some other PAM functions that can be useful.
The pam_open_session(3PAM) function is called to open a new session after a user has been successfully authenticated.
The pam_getenvlist(3PAM) function is called to establish a new environment. pam_getenvlist() returns a new environment to be merged with the existing environment.