Applications which call PAM services may be compiled as either 32-bit or 64-bit binaries.Since PAM modules are loaded as shared objects viadlopen, they mustbe provided in both 32-bit and 64-bit versions in order to support use by either form ofapplication. SeeHow to Add a PAM Module inManaging Authentication in Oracle Solaris 11.4f or details about howto install both the 32-bit and 64-bit versions of the module so that the PAM frameworkcan load the appropriate version for the application.
PAM service modules usepam_get_item(3PAM) andpam_set_item(3PAM) to communicate with applications. To communicate with each other, service modules usepam_get_data(3PAM) andpam_set_data(3PAM). If service modules from the same project need to exchange data, then a unique data name for that project should be established. The service modules can then share this data through thepam_get_data() andpam_set_data() functions.
Service modules must return one of three classes of PAM return code:
PAM_SUCCESS if the module has made a positive decision thatis part of the requested policy.
PAM_IGNORE if the module does not make a policy decision.
PAM_error if the module participates in the decision that resultsin a failure. Theerror can be either a genericerror code or a code specific to the service module type. The error cannotbe an error code for another service module type. See thepam_authtok_get(7) man page forpam_sm_module-type for theerror codes.
If a service module performs multiple functions, these functions shouldbe split up into separate modules. This approach gives system administratorsfiner-grained control for configuring policy.
Man pages should be provided for any new service modules. Man pagesshould include the following items:
Arguments that the module accepts.
All functions that the module implements.
The effect of flags on the algorithm.
Any required PAM items.
Error returns that are specific to this module.
Service modules are required to honor the PAM_SILENT flag for preventingdisplay of messages. Thedebug argument is recommendedfor logging debug information tosyslog. Usesyslog(3C) with LOG_AUTHand LOG_DEBUG for debug logging. Other messages should be sent tosyslog() with LOG_AUTH and the appropriate priority.openlog(3C),closelog(3C), andsetlogmask(3C) must notbe used as these functions interfere with the applications settings.
A sample PAM service module follows. This example checks to see if the user is a member of a group that is permitted access to this service. The provider then grants access on success or logs an error message on failure.
The example goes through the following steps:
Parse the options passed to this module from the PAM configuration. Seepam.conf(5).
This module accepts thenowarn anddebug options aswell as a specific optiongroup. With thegroup option, the module can be configured to allowaccess for a particular group other than the grouprootthat is used by default. See the definition of DEFAULT_GROUP in the sourcecode for the example. To limit access to only users who belong to groupstaff, an administrator would add the following entry to the accountconfiguration of the PAM service:
account required pam_members_only.so.1 group=staff
Get the username, service name and hostname.
The username is obtained by callingpam_get_user(3PAM) which retrieves the current user name from thePAM handle. If the user name has not been set, access is denied. The servicename and the host name are obtained by callingpam_get_item(3PAM).
Validate the information to be worked on.
If the user name is not set, deny access. If the group to be worked on isnot defined, deny access.
Verify that the current user is a member of the special group that allowsaccess to this host and grant access.
In the event that the special group is defined but contains no members atall, PAM_IGNORE is returned to indicate that this module does notparticipate in any account validation process. The decision is left to othermodules on the stack.
If the user is not a member of the special group, display a message toinform the user that access is denied.
Log a message to record this event.
The following example shows the source code for the sample PAM provider.
Example 7 Sample PAM Service Module/* * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. */#include <stdio.h>#include <stdlib.h>#include <grp.h>#include <string.h>#include <syslog.h>#include <libintl.h>#include <security/pam_appl.h>/* * by default, only users who are a member of group "root" are allowed access */#defineDEFAULT_GROUP "root"static char *NOMSG = "Sorry, you are not on the access list for this host - access denied.";intpam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc, const char **argv) { char *user = NULL; char *host = NULL; char *service = NULL; const char *allowed_grp = DEFAULT_GROUP; char grp_buf[4096]; struct group grp; struct pam_conv *conversation; struct pam_message message; struct pam_message *pmessage = &message; struct pam_response *res = NULL; int i; int nowarn = 0; int debug = 0; /* Set flags to display warnings if in debug mode. */ for (i = 0; i < argc; i++) { if (strcasecmp(argv[i], "nowarn") == 0) nowarn = 1; else if (strcasecmp(argv[i], "debug") == 0) debug = 1; else if (strncmp(argv[i], "group=", 6) == 0) allowed_grp = &argv[i][6]; } if (flags & PAM_SILENT) nowarn = 1; /* Get user name,service name, and host name. */ (void) pam_get_user(pamh, &user, NULL); (void) pam_get_item(pamh, PAM_SERVICE, (void **) &service); (void) pam_get_item(pamh, PAM_RHOST, (void **) &host); /* Deny access if user is NULL. */ if (user == NULL) { syslog(LOG_AUTH | LOG_DEBUG, "%s: members_only: user not set", service); return (PAM_USER_UNKNOWN); } if (host == NULL) host = "unknown"; /* * Deny access if vuser group is required and user is not in vuser * group */ if (getgrnam_r(allowed_grp, &grp, grp_buf, sizeof (grp_buf)) == NULL) { syslog(LOG_NOTICE | LOG_AUTH, "%s: members_only: group \"%s\" not defined", service, allowed_grp); return (PAM_SYSTEM_ERR); } /* Ignore this module if group contains no members. */ if (grp.gr_mem[0] == 0) { if (debug) syslog(LOG_AUTH | LOG_DEBUG, "%s: members_only: group %s empty: " "all users allowed.", service, grp.gr_name); return (PAM_IGNORE); } /* Check to see if user is in group. If so, return SUCCESS. */ for (; grp.gr_mem[0]; grp.gr_mem++) { if (strcmp(grp.gr_mem[0], user) == 0) { if (debug) syslog(LOG_AUTH | LOG_DEBUG, "%s: user %s is member of group %s. " "Access allowed.", service, user, grp.gr_name); return (PAM_SUCCESS); } } /* * User is not a member of the group. * Set message style to error and specify denial message. */ message.msg_style = PAM_ERROR_MSG; message.msg = gettext(NOMSG); /* Use conversation function to display denial message to user. */ (void) pam_get_item(pamh, PAM_CONV, (void **) &conversation); if (nowarn == 0 && conversation != NULL) { int err; err = conversation->conv(1, &pmessage, &res, conversation->appdata_ptr); if (debug && err != PAM_SUCCESS) syslog(LOG_AUTH | LOG_DEBUG, "%s: members_only: conversation returned " "error %d (%s).", service, err, pam_strerror(pamh, err)); /* free response (if any) */ if (res != NULL) { if (res->resp) free(res->resp); free(res); } } /* Report denial to system log and return error to caller. */ syslog(LOG_NOTICE | LOG_AUTH, "%s: members_only: " "Connection for %s not allowed from %s", service, user, host); return (PAM_PERM_DENIED);}