/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <syslog.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <strings.h>
#include <malloc.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include <libintl.h>
#include "pam_impl.h"
};
/* NONE */ NULL,
/* PAM_SERVICE */ "service",
/* PAM_USER */ "user",
/* PAM_TTY */ "tty",
/* PAM_RHOST */ "rhost",
/* PAM_CONV */ "conv",
/* PAM_AUTHTOK */ "authtok",
/* PAM_OLDAUTHTOK */ "oldauthtok",
/* PAM_RUSER */ "ruser",
/* PAM_USER_PROMPT */ "user_prompt",
/* PAM_REPOSITORY */ "repository",
/* PAM_RESOURCE */ "resource",
/* PAM_AUSER */ "auser",
/* Undefined Items */
};
/*
* This extra definition is needed in order to build this library
* on pre-64-bit-aware systems.
*/
#if !defined(_LFS64_LARGEFILE)
#endif /* !defined(_LFS64_LARGEFILE) */
/* functions to dynamically load modules */
static void *open_module(pam_handle_t *, char *);
static int load_function(void *, char *, int (**func)());
/* functions to read and store the pam.conf configuration file */
char *, boolean_t);
static void close_pam_conf(struct pam_fh *);
static int read_pam_entries(pam_handle_t *, const char *, char *, int);
static int read_pam_conf(pam_handle_t *, const char *, char *, int,
static char *read_next_token(char **);
static int verify_pam_conf(pamtab_t *, char *);
/* functions to clean up and free memory */
static void clean_up(pam_handle_t *);
static void free_pamconf(pamtab_t *);
static void free_pam_conf_info(pam_handle_t *);
static void free_resp(int, struct pam_response *);
static int do_conv(pam_handle_t *, int, int,
struct pam_response **);
static int pam_debug = 0;
static char *
{
char *name;
if (item_type <= 0 ||
item_type >= PAM_MAX_ITEMS ||
return (iname_buf);
}
return (name);
}
static char *
{
if (flag & PAM_BINDING)
return (PAM_BINDING_NAME);
if (flag & PAM_DEFINITIVE)
return (PAM_DEFINITIVE_NAME);
if (flag & PAM_INCLUDE)
return (PAM_INCLUDE_NAME);
if (flag & PAM_OPTIONAL)
return (PAM_OPTIONAL_NAME);
if (flag & PAM_REQUIRED)
return (PAM_REQUIRED_NAME);
if (flag & PAM_REQUISITE)
return (PAM_REQUISITE_NAME);
if (flag & PAM_SUFFICIENT)
return (PAM_SUFFICIENT_NAME);
return ("bad flag name");
}
static char *
{
return ("NULL");
}
#include <deflt.h>
#include <stdarg.h>
/*
* pam_settrace - setup configuration for pam tracing
*
* turn on PAM debug if "magic" file exists
* if exists (original), pam_debug = PAM_DEBUG_DEFAULT,
* log_priority = LOG_DEBUG(7) and log_facility = LOG_AUTH(4).
*
* if has contents, keywork=value pairs:
*
* "log_priority=" 0-7, the pam_trace syslog priority to use
* "log_facility=" 0-23, the pam_trace syslog facility to use
* "debug_flags=" PAM_DEBUG_DEFAULT (0x0001), log traditional
* (original) debugging.
* Plus the logical or of:
* PAM_DEBUG_ITEM (0x0002), log item values and
* pam_get_item.
* PAM_DEBUG_MODULE (0x0004), log module return status.
* PAM_DEBUG_CONF (0x0008), log pam.conf parsing.
* PAM_DEBUG_CONV (0x0020), conversation/response.
*
* If compiled with DEBUG:
* PAM_DEBUG_AUTHTOK (0x8000), display AUTHTOK value if
* PAM_DEBUG_ITEM is set and results from
* PAM_PROMPT_ECHO_OFF responses.
* USE CAREFULLY, THIS EXPOSES THE USER'S PASSWORDS.
*
* or set to 0 and off even if PAM_DEBUG file exists.
*
* Output has the general form:
* <whatever was set syslog> PAM[<pid>]: <interface>(<handle> and other info)
* <whatever was set syslog> PAM[<pid>]: details requested for <interface> call
* Where: <pid> is the process ID of the calling process.
* <handle> is the Hex value of the pam_handle associated with the
* call.
*/
static void
{
void *defp;
char *arg;
int code;
if ((code & ~LOG_PRIMASK) == 0) {
log_priority = code;
}
}
if (code < LOG_NFACILITIES) {
}
}
}
log_priority |= facility;
}
}
/*
* pam_trace - logs tracing messages
*
* format and args = message to print (PAM[<pid>]: is prepended).
*
* global log_priority = pam_trace syslog (log_priority | log_facility)
*/
/*PRINTFLIKE2*/
static void
{
int savemask;
return;
(void) setlogmask(savemask);
}
/*
* __pam_log - logs PAM syslog messages
*
* priority = message priority
* format and args = message to log
*/
/*PRINTFLIKE2*/
void
{
(void) setlogmask(savemask);
}
/*
* pam_XXXXX routines
*
* These are the entry points to the authentication switch
*/
/*
* pam_start - initiate an authentication transaction and
* set parameter values to be used during the
* transaction
*/
int
{
int err;
pam_settrace();
"pam_start(%s,%s,%p:%p) - debug = %x",
return (PAM_BUF_ERR);
!= PAM_SUCCESS) {
return (err);
}
!= PAM_SUCCESS) {
return (err);
}
!= PAM_SUCCESS) {
return (err);
}
return (PAM_SUCCESS);
}
/*
* pam_end - terminate an authentication transaction
*/
int
{
"pam_end(%p): status = %s", (void *)pamh,
return (PAM_SYSTEM_ERR);
/* call the cleanup routines for module specific data */
while (psd) {
}
p = psd;
free(p->module_data_name);
free(p);
}
/* dlclose all module fds */
while (traverse) {
}
/* remove all environment variables */
while (env_traverse) {
}
return (PAM_SUCCESS);
}
/*
* pam_set_item - set the value of a parameter that can be
* retrieved via a call to pam_get_item()
*/
int
{
int size;
"pam_set_item(%p:%s)", (void *)pamh,
}
return (PAM_SYSTEM_ERR);
/* check read only items */
return (PAM_PERM_DENIED);
/*
* Check that item_type is within valid range
*/
return (PAM_SYMBOL_ERR);
switch (item_type) {
case PAM_AUTHTOK:
case PAM_OLDAUTHTOK:
/*FALLTHROUGH*/
case PAM_SERVICE:
case PAM_USER:
case PAM_TTY:
case PAM_RHOST:
case PAM_RUSER:
case PAM_USER_PROMPT:
case PAM_RESOURCE:
case PAM_AUSER:
}
} else {
return (PAM_BUF_ERR);
}
}
break;
case PAM_CONV:
return (PAM_BUF_ERR);
else
break;
case PAM_REPOSITORY:
}
pam_repository_t *s, *d;
size = sizeof (struct pam_repository);
return (PAM_BUF_ERR);
s = (struct pam_repository *)item;
return (PAM_BUF_ERR);
return (PAM_BUF_ERR);
}
break;
default:
return (PAM_SYMBOL_ERR);
}
switch (item_type) {
case PAM_CONV:
(void *)pamh,
(void *)0);
break;
case PAM_REPOSITORY:
(void *)pamh,
"NULL");
break;
case PAM_AUTHTOK:
case PAM_OLDAUTHTOK:
#ifdef DEBUG
if (pam_debug & PAM_DEBUG_AUTHTOK)
"pam_set_item(%p:%s)=%s", (void *)pamh,
else
#endif /* DEBUG */
"pam_set_item(%p:%s)=%s", (void *)pamh,
break;
default:
(void *)pamh,
}
return (PAM_SUCCESS);
}
/*
* pam_get_item - read the value of a parameter specified in
* the call to pam_set_item()
*/
int
{
}
return (PAM_SYSTEM_ERR);
return (PAM_SYMBOL_ERR);
"a non module context",
return (PAM_PERM_DENIED);
}
switch (item_type) {
case PAM_CONV:
(void *)pamh,
break;
case PAM_REPOSITORY:
(void *)pamh,
"NULL");
break;
case PAM_AUTHTOK:
case PAM_OLDAUTHTOK:
#ifdef DEBUG
if (pam_debug & PAM_DEBUG_AUTHTOK)
"pam_get_item(%p:%s)=%s", (void *)pamh,
else
#endif /* DEBUG */
"pam_get_item(%p:%s)=%s", (void *)pamh,
break;
default:
(void *)pamh,
}
return (PAM_SUCCESS);
}
/*
* parse_user_name - process the user response: ignore
* '\t' or ' ' before or after a user name.
* user_input is a null terminated string.
* *ret_username will be the user name.
*/
static int
{
register char *ptr;
register int index = 0;
/* Set the default value for *ret_username */
*ret_username = NULL;
/*
* Set the initial value for username - this is a buffer holds
* the user name.
*/
/*
* The user_input is guaranteed to be terminated by a null character.
*/
ptr = user_input;
/* Skip all the leading whitespaces if there are any. */
ptr++;
if (*ptr == '\0') {
/*
* We should never get here since the user_input we got
* in pam_get_user() is not all whitespaces nor just "\0".
*/
return (PAM_BUF_ERR);
}
/*
* username will be the first string we get from user_input
* - we skip leading whitespaces and ignore trailing whitespaces
*/
while (*ptr != '\0') {
break;
else {
index++;
ptr++;
}
}
/* ret_username will be freed in pam_get_user(). */
return (PAM_BUF_ERR);
return (PAM_SUCCESS);
}
/*
* Get the value of PAM_USER. If not set, then use the convenience function
* to prompt for the user. Use prompt if specified, else use PAM_USER_PROMPT
* if it is set, else use default.
*/
#define WHITESPACE 0
int
{
int status;
char *real_username;
return (PAM_SYSTEM_ERR);
!= PAM_SUCCESS) {
return (status);
}
/* if the user is set, return it */
return (PAM_SUCCESS);
}
/*
* if the module is requesting a special prompt, use it.
* else use PAM_USER_PROMPT.
*/
if (prompt_override != NULL) {
prompt = (char *)prompt_override;
} else {
if (status != PAM_SUCCESS) {
return (status);
}
}
/* if the prompt is not set, use default */
}
/* prompt for the user */
for (;;) {
if (status != PAM_SUCCESS) {
return (status);
}
int i;
for (i = 0; i < len; i++) {
break;
}
}
break;
}
/* essentially empty response, try again */
}
/* set PAM_USER */
/* Parse the user input to get the user name. */
if (status != PAM_SUCCESS) {
if (real_username != NULL)
return (status);
}
if (status != PAM_SUCCESS) {
return (status);
}
/*
* finally, get PAM_USER. We have to call pam_get_item to get
* the value of user because pam_set_item mallocs the memory.
*/
return (status);
}
/*
* Set module specific data
*/
int
{
"pam_set_data(%p:%s:%d)=%p", (void *)pamh,
data);
module_data_name == NULL) {
return (PAM_SYSTEM_ERR);
}
/* check if module data already exists */
/* clean up original data before setting the new data */
}
return (PAM_SUCCESS);
}
}
return (PAM_BUF_ERR);
return (PAM_BUF_ERR);
}
return (PAM_SUCCESS);
}
/*
* get module specific data
*/
int
const void **data)
{
module_data_name == NULL) {
"pam_get_data(%p:%s:%d)=%p", (void *)pamh,
return (PAM_SYSTEM_ERR);
}
"pam_get_data(%p:%s)=%p", (void *)pamh,
module_data_name, *data);
return (PAM_SUCCESS);
}
}
"PAM_NO_MODULE_DATA");
return (PAM_NO_MODULE_DATA);
}
/*
* PAM equivalent to strerror()
*/
/* ARGSUSED */
const char *
{
switch (errnum) {
case PAM_SUCCESS:
case PAM_OPEN_ERR:
case PAM_SYMBOL_ERR:
case PAM_SERVICE_ERR:
return (dgettext(TEXT_DOMAIN,
"Error in underlying service module"));
case PAM_SYSTEM_ERR:
case PAM_BUF_ERR:
case PAM_CONV_ERR:
case PAM_PERM_DENIED:
case PAM_MAXTRIES:
return (dgettext(TEXT_DOMAIN,
"Maximum number of attempts exceeded"));
case PAM_AUTH_ERR:
case PAM_NEW_AUTHTOK_REQD:
case PAM_CRED_INSUFFICIENT:
case PAM_AUTHINFO_UNAVAIL:
return (dgettext(TEXT_DOMAIN,
"Can not retrieve authentication info"));
case PAM_USER_UNKNOWN:
case PAM_CRED_UNAVAIL:
return (dgettext(TEXT_DOMAIN,
"Can not retrieve user credentials"));
case PAM_CRED_EXPIRED:
return (dgettext(TEXT_DOMAIN,
"User credentials have expired"));
case PAM_CRED_ERR:
return (dgettext(TEXT_DOMAIN,
"Failure setting user credentials"));
case PAM_ACCT_EXPIRED:
case PAM_AUTHTOK_EXPIRED:
case PAM_SESSION_ERR:
return (dgettext(TEXT_DOMAIN,
case PAM_AUTHTOK_ERR:
return (dgettext(TEXT_DOMAIN,
"Authentication token manipulation error"));
case PAM_AUTHTOK_RECOVERY_ERR:
return (dgettext(TEXT_DOMAIN,
"Authentication token can not be recovered"));
case PAM_AUTHTOK_LOCK_BUSY:
return (dgettext(TEXT_DOMAIN,
"Authentication token lock busy"));
return (dgettext(TEXT_DOMAIN,
"Authentication token aging disabled"));
case PAM_NO_MODULE_DATA:
return (dgettext(TEXT_DOMAIN,
"Module specific data not found"));
case PAM_IGNORE:
case PAM_ABORT:
case PAM_TRY_AGAIN:
return (dgettext(TEXT_DOMAIN,
"Unable to complete operation. Try again"));
default:
}
}
static void *
{
switch (ind) {
case PAM_AUTHENTICATE:
return (PAM_SM_AUTHENTICATE);
case PAM_SETCRED:
return (PAM_SM_SETCRED);
case PAM_ACCT_MGMT:
return (PAM_SM_ACCT_MGMT);
case PAM_OPEN_SESSION:
return (PAM_SM_OPEN_SESSION);
case PAM_CLOSE_SESSION:
return (PAM_SM_CLOSE_SESSION);
case PAM_CHAUTHTOK:
return (PAM_SM_CHAUTHTOK);
}
return (NULL);
}
static int
{
void *funcp;
return (NULL);
switch (ind) {
case PAM_AUTHENTICATE:
case PAM_SETCRED:
case PAM_ACCT_MGMT:
case PAM_OPEN_SESSION:
case PAM_CLOSE_SESSION:
case PAM_CHAUTHTOK:
}
return (NULL);
}
/*
* Run through the PAM service module stack for the given module type.
*/
static int
{
int optional_error = 0;
int required_error = 0;
int success = 0;
int (*sm_func)();
char *service;
return (PAM_SYSTEM_ERR);
/*
* pam_eval(3PAM) calls run_stack() to evaluate a supplied PAM
* configuration file if pam_user_policy(5) is configured. On the
* first pass through run_stack() we stash away the passed in
* arguments so we can pass the same ones to run_stack() if called
* by pam_eval(3PAM) later.
*/
if (!pamh->pam_eval_called) {
}
return (PAM_SYSTEM_ERR);
}
/*
* from pam_eval(3PAM) which is invoked by pam_user_policy(5).
* pam_config will be the path to a PAM configuration file if called
* from pam_eval(3PAM), otherwise it is NULL.
*/
type)) != PAM_SUCCESS) {
if (pam_config == NULL) {
"no valid PAM configuration entries found in %s, "
} else if (err != PAM_IGNORE) {
"no valid PAM configuration entries found in %s",
}
goto exit_return;
}
if (!pamh->pam_eval_called) {
}
include:
/* save the return location */
"setting for include[%d:%p]",
"run_stack: includes too deep %d "
"found trying to include %s from %s, %d "
goto exit_return;
}
"run_stack[%d:%s]: can't open included "
"conf %s or no valid PAM configuration "
"entries for %s were found in %s",
goto exit_return;
}
/* first line another include */
goto include;
}
"[%d:%s]:%s(%p, %x): load_modules failed",
goto exit_return;
}
modulep)) != PAM_SUCCESS) {
"[%d:%s]:%s(%p, %x): load_modules failed",
goto exit_return;
} /* PAM_INCLUDE */
if (sm_func) {
(const char **)modulep->module_argv);
"[%d:%s]:%s(%p, %x): %s returned %s",
switch (err) {
case PAM_IGNORE:
/* do nothing */
break;
case PAM_SUCCESS:
!required_error) {
"[%d:%s]:%s(%p, %x): %s: success",
goto exit_return;
}
success = 1;
break;
case PAM_TRY_AGAIN:
/*
* We need to return immediately, and
* we shouldn't reset the AUTHTOK item
* since it is not an error per-se.
*/
"[%d:%s]:%s(%p, %x): TRY_AGAIN: %s",
required_error : err));
goto exit_return;
default:
"[%d:%s]:%s(%p, %x): %s: %s",
err));
err = required_error ?
goto exit_return;
if (!required_error)
} else {
if (!optional_error)
}
"[%d:%s]:%s(%p, %x): error %s",
break;
}
}
}
if ((pamh->pam_eval_called &&
pamh->include_depth--;
/* continue at next entry */
goto include;
}
if (!pamh->pam_eval_called) {
}
if (required_error != 0)
return (required_error);
else if (success != 0)
return (PAM_SUCCESS);
else if (optional_error != 0)
return (optional_error);
else
return (def_err);
/*
* All done at whatever depth we're at.
*/
while ((pamh->pam_eval_called &&
pamh->include_depth--;
}
if (!pamh->pam_eval_called) {
}
return (err);
}
/*
* pam_authenticate - authenticate a user
*/
int
{
int retval;
if (retval != PAM_SUCCESS)
return (retval);
}
/*
* pam_setcred - modify or retrieve user credentials
*/
int
{
int retval;
if (retval != PAM_SUCCESS)
return (retval);
}
/*
* pam_acct_mgmt - check password aging, account expiration
*/
int
{
int retval;
if (retval != PAM_SUCCESS &&
retval != PAM_NEW_AUTHTOK_REQD) {
}
return (retval);
}
/*
* pam_open_session - begin session management
*/
int
{
int retval;
if (retval != PAM_SUCCESS)
return (retval);
}
/*
* pam_close_session - terminate session management
*/
int
{
int retval;
if (retval != PAM_SUCCESS)
return (retval);
}
/*
* pam_chauthtok - change user authentication token
*/
int
{
int retval;
/* do not let apps use PAM_PRELIM_CHECK or PAM_UPDATE_AUTHTOK */
return (PAM_SYMBOL_ERR);
}
/* 1st pass: PRELIM CHECK */
"pam_chauthtok-prelim");
if (retval == PAM_TRY_AGAIN)
return (retval);
if (retval != PAM_SUCCESS) {
return (retval);
}
/* 2nd pass: UPDATE AUTHTOK */
"pam_chauthtok-update");
if (retval != PAM_SUCCESS)
return (retval);
}
/*
* pam_putenv - add an environment variable to the PAM handle
* if name_value == 'NAME=VALUE' then set variable to the value
* if name_value == 'NAME=' then set variable to an empty value
* if name_value == 'NAME' then delete the variable
*/
int
{
char *equal_sign = 0;
"pam_putenv(%p, %s)", (void *)pamh,
goto out;
/* see if we were passed 'NAME=VALUE', 'NAME=', or 'NAME' */
sizeof (char))) == 0) {
error = PAM_BUF_ERR;
goto out;
}
error = PAM_BUF_ERR;
goto out;
}
} else {
error = PAM_BUF_ERR;
goto out;
}
}
/* check to see if we already have this variable in the PAM handle */
}
if (traverse) {
/* found a match */
if (value == 0) {
/* remove the env variable */
else
/* set env variable to empty value */
goto out;
}
} else {
/* set the new value */
goto out;
}
}
/*
* could not find a match in the PAM handle.
* add the new value if there is one
*/
error = PAM_BUF_ERR;
goto out;
}
error = PAM_BUF_ERR;
goto out;
}
error = PAM_BUF_ERR;
goto out;
}
if (trail == 0) {
/* new head of list */
} else {
/* adding to end of list */
}
}
error = PAM_SUCCESS;
out:
if (error != PAM_SUCCESS) {
if (traverse) {
}
}
if (name)
if (value)
return (error);
}
/*
* pam_getenv - retrieve an environment variable from the PAM handle
*/
char *
{
goto out;
/* check to see if we already have this variable in the PAM handle */
}
out:
}
/*
* pam_getenvlist - retrieve all environment variables from the PAM handle
* in a NULL terminated array. On error, return NULL.
*/
char **
{
char **list = 0;
int length = 0;
char *tenv;
"pam_getenvlist(%p)", (void *)pamh);
goto out;
/* find out how many environment variables we have */
while (traverse) {
length++;
}
/* allocate the array we will return to the caller */
error = PAM_BUF_ERR;
goto out;
}
/* add the variables one by one */
length = 0;
error = PAM_BUF_ERR;
goto out;
}
/*LINTED*/
}
error = PAM_SUCCESS;
out:
if (error != PAM_SUCCESS) {
/* free the partially constructed list */
if (list) {
length = 0;
length++;
}
}
}
}
int
{
int retval;
int fd;
/* a missing pam.conf-formatted file or an empty path is an error */
"an empty or missing path");
return (PAM_SYSTEM_ERR);
}
/*
* pam_eval() can only be called by PAM service provider (pam_sm_*)
* routines (viz. from a module context).
*/
"a non module context");
return (PAM_PERM_DENIED);
}
/*
* It doesn't make sense for pam_eval() to be called more than
* once in a single transaction so any attempts to do so are denied.
*/
if (pamh->pam_eval_called) {
"pam_eval(): nested pam_user_policy(5) modules aren't "
"valid. pam_user_policy(5) referenced a second time in: "
"%s", conf_path);
return (PAM_SYSTEM_ERR);
}
/*
* The pathname argument to pam_eval(3PAM), which points to a
* pam.conf(4)-formatted 'pam_policy' file, must be an absolute
* pathname. pam_user_policy(5) qualifies the pathnames it
* processes by prepending PAM_POLICY_DIR
* (/etc/security/pam_policy) to regular filenames.
*/
if (*conf_path != '/') {
"pam_eval() invalid path supplied: %s", conf_path);
return (PAM_SYSTEM_ERR);
}
/*
* Verify the PAM configuration file exists. Further stringency
* checks such as permissions, ownership, size, and contents happen
* later on. We check that the filename exists here so we can
* report the precise root cause since open(2) failures in
* open_pam_conf() aren't syslogged since this is a common
* is frequently missing.
*/
"pam_eval() PAM configuration file '%s' does not exist",
return (PAM_SYSTEM_ERR);
}
pamh->include_depth++;
/*
* Inside run_stack() further include directives may be processed
* so set a marker here so that we only "pop" includes added after
* this call to pam_eval() and not any includes added before.
*/
pamh->include_depth--;
return (retval);
}
/*
* Routines to load a requested module on demand
*/
/*
* load_modules - load the requested module.
* if the dlopen or dlsym fail, then
* the module is ignored.
*/
static int
{
void *mh;
"while load_modules[%d:%s](%p, %s)=%s",
"done load_modules[%d:%s](%p, %s)=%s",
(void *)pamh, function_name,
return (PAM_SUCCESS);
}
switch (type) {
case PAM_AUTH_MODULE:
/* if the function has already been loaded, return */
if (!loading_functions &&
return (PAM_SUCCESS);
}
/* function has not been loaded yet */
loading_functions = 1;
return (PAM_BUF_ERR);
}
/* if open_module fails, return error */
"load_modules[%d:%s]: can not open module "
return (PAM_OPEN_ERR);
}
/* load the authentication function */
!= PAM_SUCCESS) {
/* return error if dlsym fails */
return (PAM_SYMBOL_ERR);
}
/* load the setcred function */
/* return error if dlsym fails */
return (PAM_SYMBOL_ERR);
}
}
break;
case PAM_ACCOUNT_MODULE:
if (!loading_functions &&
return (PAM_SUCCESS);
}
/*
* If functions are added to the account module,
* verify that one of the other functions hasn't
* already loaded it. See PAM_AUTH_MODULE code.
*/
loading_functions = 1;
return (PAM_BUF_ERR);
/* if open_module fails, return error */
"load_modules[%d:%s]: can not open module "
return (PAM_OPEN_ERR);
}
"load_modules[%d:%s]: pam_sm_acct_mgmt() "
return (PAM_SYMBOL_ERR);
}
break;
case PAM_SESSION_MODULE:
if (!loading_functions &&
(((strcmp(function_name,
PAM_SM_OPEN_SESSION) == 0) &&
PAM_SM_CLOSE_SESSION) == 0) &&
return (PAM_SUCCESS);
}
loading_functions = 1;
sizeof (struct session_module));
return (PAM_BUF_ERR);
}
/* if open_module fails, return error */
"load_modules[%d:%s]: can not open module "
return (PAM_OPEN_ERR);
}
return (PAM_SYMBOL_ERR);
} else if ((strcmp(function_name,
PAM_SM_CLOSE_SESSION) == 0) &&
return (PAM_SYMBOL_ERR);
}
break;
case PAM_PASSWORD_MODULE:
if (!loading_functions &&
return (PAM_SUCCESS);
}
/*
* If functions are added to the password module,
* verify that one of the other functions hasn't
* already loaded it. See PAM_AUTH_MODULE code.
*/
loading_functions = 1;
return (PAM_BUF_ERR);
/* if open_module fails, continue */
"load_modules[%d:%s]: can not open module "
return (PAM_OPEN_ERR);
}
return (PAM_SYMBOL_ERR);
}
break;
default:
"load_modules[%d:%s](%p, %s): unsupported type %d",
break;
}
} /* while */
return (PAM_SUCCESS);
}
/*
* open_module - Open the module first checking for
* propers modes and ownerships on the file.
*/
static void *
{
char *errmsg;
void *lfd;
/* Check the ownership and file modes */
"open_module[%d:%s]: stat(%s) failed: %s",
return (NULL);
}
"open_module[%d:%s]: Owner of the module %s is not root",
return (NULL);
}
"open_module[%d:%s]: module %s writable by group",
return (NULL);
}
"open_module[%d:%s]: module %s writable by world",
return (NULL);
}
/*
* Perform the dlopen()
*/
return (NULL);
} else {
/* add this fd to the pam handle */
lfd = 0;
return (NULL);
}
/* adding new head of list */
} else {
/* appending to end of list */
while (traverse) {
}
}
}
return (lfd);
}
/*
* load_function - call dlsym() to resolve the function address
*/
static int
{
return (PAM_SYMBOL_ERR);
return (PAM_SYMBOL_ERR);
}
"load_function: successful load of %s", name);
return (PAM_SUCCESS);
}
/*
* Routines to read the pam.conf configuration file
*/
/*
* open_pam_conf - open the PAM configuration file which may be in
* either the traditional pam.conf(4) syntax or the per-service
* field for the 'service' isn't pressent.
*/
static int
{
int fd;
/*
* 'config' argument to read_pam_conf() is NULL. In such cases, we
* case-insensitive match of a pam.d/<service> file to the current
* service name (the named service is passed in first and if no match
* is found "other" is passed in as the service name). This is to
* maintain the same behaviour of pam.conf(4) where service names are
* case-insensitive.
*/
"open_pam_conf[%d:%s/%s]: "
"couldn't open directory %s: %s",
return (0);
}
continue;
}
config = pam_config;
break;
}
}
"open_pam_conf[%d:%s/%s]: failed to find: "
return (0);
}
}
"open_pam_conf[%d:%s]: open(%s) failed: %s",
return (0);
}
/* Check the ownership and file modes; confirm it's a regular file */
"open_pam_conf[%d:%s]: stat(%s) failed: %s",
return (0);
}
"open_pam_conf[%d:%s]: %s is not a regular file",
return (0);
}
"open_pam_conf[%d:%s]: %s has a size of zero",
return (0);
}
"open_pam_conf[%d:%s]: Owner of %s is not root",
return (0);
}
"open_pam_conf[%d:%s]: %s writable by group",
return (0);
}
"open_pam_conf[%d:%s]: %s writable by world",
return (0);
}
return (0);
}
return (0);
}
return (1);
}
/*
* close_pam_conf - close pam.conf
*/
static void
{
}
/*
*/
static int
int type)
{
/*
* default run_stack() scenario, no pam_eval(3PAM) or 'include'
* directives involved. The search order is as follows:
* which retains backwards compatbility with pam.conf(4) and
*/
if (pam_config == NULL) {
B_FALSE) != PAM_SUCCESS) {
B_TRUE) != PAM_SUCCESS) {
}
}
}
} else {
/*
* A specific PAM configuration file is being processed due to
* either pam_eval(3PAM) or an 'include' directive. The
* included file could have either the pam.conf(4) syntax with
* syntax with the 'service' name omitted. Thus we look for
* PAM entries for the specified service and the 'other'
* service using both formats.
*/
B_FALSE) != PAM_SUCCESS) {
B_FALSE) != PAM_SUCCESS) {
}
}
}
}
return (PAM_SUCCESS);
}
/*
* read_pam_conf - read in each entry in pam.conf and store info
* under the pam handle.
*/
static int
{
int error;
/*
* service types:
* error (-1), "auth" (0), "account" (1), "session" (2), "password" (3)
*/
return (PAM_SYSTEM_ERR);
}
pamdotd, i)) == PAM_SUCCESS &&
pamentp) {
/* See if entry is this service and valid */
"read_pam_conf[%d:%s](%p): bad entry error %s",
goto out;
}
"read_pam_conf[%d:%s](%p): processing %s",
/* process first service entry */
/* purge "other" entries */
"read_pam_conf(%p): purging "
"\"other\"[%d:%s][%s]",
(void *)pamh, i,
pamh->pam_conf_info[i]
}
/* add first service entry */
"read_pam_conf(%p): adding 1st "
"%s[%d:%s][%s]",
} else {
/* append more service entries */
"read_pam_conf(%p): adding more "
"%s[%d:%s][%s]",
tpament =
}
}
} else {
/* irrelevant entry */
}
}
out:
/* no entry found for the specified service module type */
}
"service = %s type = %d pam.d = %d error = %d", i,
(void) close_pam_conf(pam_fh);
if (error != PAM_SUCCESS)
return (error);
}
/*
* get_pam_conf_entry - get a pam.conf entry
*/
static int
{
int argc;
int i;
int err;
/* get the next line from pam.conf */
/* no more lines in pam.conf ==> return */
error = PAM_SUCCESS;
goto out;
}
goto out;
}
/* copy full line for error reporting */
goto out;
}
/* get service name (e.g. login, su, passwd) */
if (!skip_service) {
"illegal pam.conf[%s] entry: %s: "
goto out;
}
/*
* pam_eval(3PAM) and the 'include' directive may reference
* PAM policy files in either pam.conf(4) format or the
* The pam.conf(4) format is checked first so when looking for
* pam.conf(4) syntax and no service entry is present stop
* searching using pam.conf(4) syntax. This avoids syslog'ing
*/
if ((include_depth > 0) &&
"pam.conf[%s] entry: %s: missing SERVICE name",
goto out;
}
goto out;
}
} else {
goto out;
}
}
/* get module type (e.g. authentication, acct mgmt) */
"illegal pam.conf[%s] entry: %s: missing MODULE TYPE",
goto getflag;
}
} else {
/*
* If the token read isn't a PAM service module type then it is
* an error. However since we support both the pam.conf(4)
* format and the pam.d format for included PAM configs and PAM
* policy files specified for pam_eval() we handle the case of
* a partial PAM policy file here in pam.conf syntax but
* searching using pam.d syntax by skipping the first token
* which should be the PAM_SERVICE and then continuing with the
* format verification of the rest of the line.
*/
if (skip_service && (include_depth > 0)) {
goto getmodule_type;
} else {
"illegal pam.conf[%s] entry: %s: invalid module "
arg);
}
}
/* get pam flag (e.g., requisite, required, sufficient, optional) */
"illegal pam.conf[%s] entry: %s: missing CONTROL FLAG",
goto getpath;
}
} else {
/* error */
"illegal pam.conf[%s] entry: %s",
"\tinvalid control flag: %s", arg);
}
/* get module path (e.g. /usr/lib/security/pam_unix_auth.so.1) */
"illegal pam.conf[%s] entry: %s: missing MODULE PATH",
goto out;
}
if (arg[0] != '/') {
/*
* If module path does not start with "/", then
*/
/* sizeof (PAM_LIB_DIR) has room for '\0' */
goto out;
}
PAM_LIB_DIR, arg);
} else {
}
} else {
/* Full path provided for module */
char *isa;
/* Check for Instruction Set Architecture indicator */
sizeof (PAM_ISA_DIR);
/* substitute the architecture dependent path */
"strdup: out of memory");
goto out;
}
*isa = '\000';
goto out;
}
}
/* count the number of module-specific options first */
argc = 0;
goto out;
}
argc++;
/* allocate array for the module-specific options */
if (argc > 0) {
if (((*pam)->module_argv =
goto out;
}
i = 0;
goto out;
}
i++;
}
}
out:
if (current_line)
if (error != PAM_SUCCESS) {
/* on error free this */
if (*pam)
free_pamconf(*pam);
}
return (error);
}
/*
* read_next_token - skip tab and space characters and return the next token
*/
static char *
{
char *start;
if (cp == (char *)0) {
*cpp = (char *)0;
return ((char *)0);
}
cp++;
if (*cp == '\0') {
*cpp = (char *)0;
return ((char *)0);
}
cp++;
if (*cp != '\0')
*cp++ = '\0';
return (start);
}
static char *
{
while (count) {
if (*sp == (char)c)
return ((char *)sp);
else {
sp++;
count--;
}
};
return (NULL);
}
/*
* nextline - skip all blank lines and comments
*/
static char *
{
char *ll;
int find_a_line = 0;
/*
* Skip the blank line, comment line
*/
while (!find_a_line) {
/* if we are at the end of the buffer, there is no next line */
if (bufferp == bufferendp)
return (NULL);
/* skip blank line */
while (*bufferp == '\n') {
/*
* If we are at the end of the buffer, there is
* no next line.
*/
if (++bufferp == bufferendp) {
return (NULL);
}
/* else we check *bufferp again */
}
/* skip comment line */
while (*bufferp == '#') {
} else {
/*
* this comment line the last line.
* no next line
*/
return (NULL);
}
/*
* If we are at the end of the buffer, there is
* no next line.
*/
if (bufferp == bufferendp) {
return (NULL);
}
}
find_a_line = 1;
}
}
*err = PAM_SUCCESS;
/* now we find one line */
!= NULL) {
"nextline[%d:%s]: pam.conf line too long %.256s",
bufferp);
*err = PAM_SERVICE_ERR;
}
} else {
ll = bufferendp;
"nextline[%d:%s]: pam.conf line too long %.256s",
bufferp);
*err = PAM_SERVICE_ERR;
}
}
}
/*
* verify_pam_conf - verify that the pam_conf entry is filled in.
*
* True = Error if there is no service.
* True = Error if there is a service and it matches the requested service
* but, the type, flag, line overflow, or path is in error.
*/
static int
{
}
/*
* Routines to free allocated storage
*/
/*
* clean_up - free allocated storage in the pam handle
*/
static void
{
int i;
if (pamh) {
while (pamh->include_depth >= 0) {
pamh->include_depth--;
}
/* Cleanup PAM_REPOSITORY structure */
}
for (i = 0; i < PAM_MAX_ITEMS; i++) {
if (i == PAM_AUTHTOK || i == PAM_OLDAUTHTOK) {
}
}
}
}
}
/*
* free_pamconf - free memory used to store pam.conf entry
*/
static void
{
int i;
if (cp) {
if (cp->pam_service)
if (cp->module_path)
for (i = 0; i < cp->module_argc; i++) {
if (cp->module_argv[i])
}
if (cp->module_argc > 0)
if (cp->function_ptr)
}
}
/*
* free_pam_conf_info - free memory used to store all pam.conf info
* under the pam handle
*/
static void
{
int i = pamh->include_depth;
int j;
for (j = 0; j < PAM_NUM_MODULE_TYPES; j++) {
while (pamentp) {
}
}
}
}
static void
{
if (pam_env) {
}
}
/*
* Internal convenience functions for Solaris PAM service modules.
*/
#include <libintl.h>
#include <nl_types.h>
#include <synch.h>
#include <locale.h>
#include <thread.h>
typedef struct pam_msg_data {
/*
* free_resp():
* free storage for responses used in the call back "pam_conv" functions
*/
void
{
int i;
struct pam_response *r;
if (resp) {
r = resp;
for (i = 0; i < num_msg; i++, r++) {
if (r->resp) {
/* clear before freeing -- may be a password */
}
}
}
}
static int
struct pam_response *ret_respp[])
{
struct pam_message *m;
int i;
int k;
int retcode;
(void **)&pam_convp)) != PAM_SUCCESS) {
return (retcode);
}
/*
* When pam_set_item() is called to set PAM_CONV and the
* item is NULL, memset(pip->pi_addr, 0, size) is called.
* So at this point, we should check whether pam_convp->conv
* is NULL or not.
*/
return (PAM_SYSTEM_ERR);
i = 0;
k = num_msg;
return (PAM_BUF_ERR);
}
m = msg;
while (k--) {
/*
* fill out the message structure to display prompt message
*/
"pam_conv_msg(%p:%d[%d]=%s)",
m++;
i++;
}
/*
* The UNIX pam modules always calls __pam_get_authtok() and
* __pam_display_msg() with a NULL pointer as the conv_apdp.
* In case the conv_apdp is NULL and the pam_convp->appdata_ptr
* is not NULL, we should pass the pam_convp->appdata_ptr
* to the conversation function.
*/
/*
* Call conv function to display the prompt.
*/
"pam_conv_resp(%p pam_conv = %s) ret_respp = %p",
"pam_conv_resp(%p No response requested)", (void *)pamh);
} else {
struct pam_response *r = *ret_respp;
for (i = 0; i < num_msg; i++, r++) {
"pam_conv_resp(%p:"
"[%d] NULL response string)",
(void *)pamh, i);
} else {
"pam_conv_resp(%p:[%d] len=%lu, "
"code=%d too long)",
(void *)pamh, i,
r->resp_retcode);
break;
}
if (msg_style == PAM_PROMPT_ECHO_OFF) {
#ifdef DEBUG
"pam_conv_resp(%p:[%d]=%s, "
"code=%d)",
r->resp_retcode);
#endif /* DEBUG */
"pam_conv_resp(%p:[%d] len=%lu, "
"code=%d)",
(void *)pamh, i,
r->resp_retcode);
} else {
"pam_conv_resp(%p:[%d]=%s, "
"code=%d)",
r->resp_retcode);
}
}
}
}
if (msg)
return (retcode);
}
/*
* __pam_display_msg():
* display message by calling the call back functions
* provided by the application through "pam_conv" structure
*/
int
{
int ret;
return (ret);
}
/*
* __pam_get_authtok()
* retrieves a password of at most PASS_MAX length from the pam
* handle (pam_get_item) or from the input stream (do_conv).
*
* This function allocates memory for the new authtok.
* Applications calling this function are responsible for
* freeing this memory.
*
* If "source" is
* PAM_HANDLE
* and "type" is:
* PAM_AUTHTOK - password is taken from pam handle (PAM_AUTHTOK)
* PAM_OLDAUTHTOK - password is taken from pam handle (PAM_OLDAUTHTOK)
*
* If "source" is
* PAM_PROMPT
* and "type" is:
* 0: Prompt for new passwd, do not even attempt
* to store it in the pam handle.
* PAM_AUTHTOK: Prompt for new passwd, store in pam handle as
* PAM_AUTHTOK item if this value is not already set.
* PAM_OLDAUTHTOK: Prompt for new passwd, store in pam handle as
* PAM_OLDAUTHTOK item if this value is not
* already set.
*/
int
char **authtok)
{
return (PAM_BUF_ERR);
switch (source) {
case PAM_HANDLE:
/* get password from pam handle item list */
switch (type) {
case PAM_AUTHTOK:
case PAM_OLDAUTHTOK:
(void **)&new_password)) != PAM_SUCCESS)
goto err_ret;
} else {
PASS_MAX+1);
}
break;
default:
"__pam_get_authtok() invalid type: %d", type);
goto err_ret;
}
break;
case PAM_PROMPT:
/*
* Prompt for new password and save in pam handle item list
* if the that item is not already set.
*/
goto err_ret;
/* getpass didn't return anything */
goto err_ret;
}
/* save the new password if this item was NULL */
if (type) {
(void **)&new_password)) != PAM_SUCCESS) {
goto err_ret;
}
if (new_password == NULL)
}
break;
default:
"__pam_get_authtok() invalid source: %d", source);
goto err_ret;
}
return (PAM_SUCCESS);
return (error);
}