1820 lines
40 KiB
C
1820 lines
40 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU Lesser General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with these librararies and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
/* $XConsortium: pam_framework.c /main/6 1996/11/01 10:12:32 drk $ */
|
|
/*
|
|
* Copyright (c) 1992-1995, by Sun Microsystems, Inc.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
|
|
|
|
#include <syslog.h>
|
|
|
|
#ifdef sun
|
|
#ident "@(#)pam_framework.c 1.74 96/02/15 SMI"
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
#ifdef hpV4
|
|
#include <dl.h>
|
|
#endif
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include <security/pam_appl.h>
|
|
#include <security/pam_modules.h>
|
|
#include "pam_impl.h"
|
|
#include "pam_loc.h"
|
|
|
|
#include <X11/Xos.h>
|
|
#include <errno.h>
|
|
#ifdef X_NOT_STDC_ENV
|
|
extern int errno;
|
|
#endif
|
|
|
|
#ifdef sun
|
|
#define dlopen _dlopen
|
|
#define dlclose _dlclose
|
|
#define dlsym _dlsym
|
|
#define dlerror _dlerror
|
|
#endif
|
|
|
|
/* PAM debugging */
|
|
#define PAM_DEBUG "/etc/pam_debug"
|
|
|
|
|
|
static int pam_debug = 0;
|
|
|
|
/* functions to dynamically load modules */
|
|
static int load_modules(pam_handle_t *, int, char *);
|
|
#ifdef sun
|
|
static void *open_module(char *);
|
|
static int load_function(void *, char *, int (**func)());
|
|
#endif
|
|
#ifdef hpV4
|
|
static shl_t open_module(char *);
|
|
static int load_function(shl_t, char *, int (**func)());
|
|
#endif
|
|
|
|
/* functions to read and store the pam.conf configuration file */
|
|
static int open_pam_conf(struct pam_fh **);
|
|
static void close_pam_conf(struct pam_fh *);
|
|
static int read_pam_conf(pam_handle_t *);
|
|
static int get_pam_conf_entry(struct pam_fh *, pamtab **);
|
|
static char *read_next_token(char **);
|
|
static char *nextline(struct pam_fh *);
|
|
|
|
/* functions to clean up and free memory */
|
|
static void clean_up(pam_handle_t *);
|
|
static void free_pamconf(pamtab *);
|
|
static void free_pam_conf_info(pam_handle_t *);
|
|
|
|
/*
|
|
* 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
|
|
pam_start(
|
|
const char *service,
|
|
const char *user,
|
|
const struct pam_conv *pam_conv,
|
|
pam_handle_t **pamh)
|
|
{
|
|
struct stat statbuf;
|
|
int err;
|
|
|
|
/* turn on PAM debug if "magic" file exists */
|
|
|
|
if (stat(PAM_DEBUG, &statbuf) == 0) {
|
|
pam_debug = 1;
|
|
openlog("PAM", LOG_CONS|LOG_NDELAY, LOG_AUTH);
|
|
}
|
|
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG, "pam_start(%s %s)",
|
|
service, (user)?user:"no-user");
|
|
|
|
*pamh = (struct pam_handle *) calloc(1, sizeof (struct pam_handle));
|
|
if (*pamh == NULL)
|
|
return (PAM_BUF_ERR);
|
|
|
|
if ((err = pam_set_item(*pamh, PAM_SERVICE, (void *)service))
|
|
!= PAM_SUCCESS) {
|
|
clean_up(*pamh);
|
|
*pamh = NULL;
|
|
return (err);
|
|
}
|
|
|
|
if ((err = pam_set_item(*pamh, PAM_USER, (void *) user))
|
|
!= PAM_SUCCESS) {
|
|
clean_up(*pamh);
|
|
*pamh = NULL;
|
|
return (err);
|
|
}
|
|
|
|
if ((err = pam_set_item(*pamh, PAM_CONV, (void *) pam_conv))
|
|
!= PAM_SUCCESS) {
|
|
clean_up(*pamh);
|
|
*pamh = NULL;
|
|
return (err);
|
|
}
|
|
|
|
/* read all the entries from pam.conf */
|
|
if ((err = read_pam_conf (*pamh)) != PAM_SUCCESS) {
|
|
clean_up (*pamh);
|
|
*pamh = NULL;
|
|
return (err);
|
|
}
|
|
|
|
return (PAM_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* pam_end - terminate an authentication transaction
|
|
*/
|
|
|
|
int
|
|
pam_end(pam_handle_t *pamh, int pam_status)
|
|
{
|
|
struct pam_module_data *psd, *p;
|
|
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG, "pam_end(): status = %s",
|
|
pam_strerror(pamh, pam_status));
|
|
|
|
if (pamh == NULL)
|
|
return (PAM_SYSTEM_ERR);
|
|
|
|
/* call the cleanup routines for module specific data */
|
|
|
|
psd = pamh->ssd;
|
|
while (psd) {
|
|
if (psd->cleanup) {
|
|
psd->cleanup(pamh, psd->data, pam_status);
|
|
}
|
|
p = psd;
|
|
psd = p->next;
|
|
free(p->module_data_name);
|
|
free(p);
|
|
}
|
|
pamh->ssd = NULL;
|
|
|
|
clean_up(pamh);
|
|
|
|
/* end syslog reporting */
|
|
|
|
if (pam_debug)
|
|
closelog();
|
|
|
|
return (PAM_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* pam_set_item - set the value of a parameter that can be
|
|
* retrieved via a call to pam_get_item()
|
|
*/
|
|
|
|
int
|
|
pam_set_item(
|
|
pam_handle_t *pamh,
|
|
int item_type,
|
|
const void *item)
|
|
{
|
|
struct pam_item *pip;
|
|
int size;
|
|
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG, "pam_set_item(%d)", item_type);
|
|
|
|
if (pamh == NULL)
|
|
return (PAM_SYSTEM_ERR);
|
|
/*
|
|
* Check that item_type is within valid range
|
|
*/
|
|
|
|
if (item_type <= 0 || item_type >= PAM_MAX_ITEMS)
|
|
return (PAM_SYMBOL_ERR);
|
|
|
|
pip = &(pamh->ps_item[item_type]);
|
|
|
|
switch (item_type) {
|
|
case PAM_AUTHTOK:
|
|
case PAM_OLDAUTHTOK:
|
|
if (pip->pi_addr != NULL)
|
|
memset(pip->pi_addr, 0, pip->pi_size);
|
|
case PAM_SERVICE:
|
|
case PAM_USER:
|
|
case PAM_TTY:
|
|
case PAM_RHOST:
|
|
case PAM_RUSER:
|
|
case PAM_USER_PROMPT:
|
|
if (pip->pi_addr != NULL) {
|
|
free(pip->pi_addr);
|
|
}
|
|
|
|
if (item == NULL) {
|
|
pip->pi_addr = NULL;
|
|
pip->pi_size = 0;
|
|
} else {
|
|
pip->pi_addr = strdup((char *)item);
|
|
if (pip->pi_addr == NULL) {
|
|
pip->pi_size = 0;
|
|
return (PAM_BUF_ERR);
|
|
}
|
|
pip->pi_size = strlen(pip->pi_addr);
|
|
}
|
|
break;
|
|
case PAM_CONV:
|
|
if (pip->pi_addr != NULL)
|
|
free(pip->pi_addr);
|
|
size = sizeof (struct pam_conv);
|
|
if ((pip->pi_addr = (void *) calloc(1, size)) == NULL)
|
|
return (PAM_BUF_ERR);
|
|
if (item != NULL)
|
|
(void) memcpy(pip->pi_addr, item,
|
|
(unsigned int) size);
|
|
else
|
|
memset(pip->pi_addr, 0, size);
|
|
pip->pi_size = size;
|
|
break;
|
|
default:
|
|
return (PAM_SYMBOL_ERR);
|
|
}
|
|
|
|
return (PAM_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* pam_get_item - read the value of a parameter specified in
|
|
* the call to pam_set_item()
|
|
*/
|
|
|
|
int
|
|
pam_get_item(
|
|
const pam_handle_t *pamh,
|
|
int item_type,
|
|
void **item)
|
|
{
|
|
struct pam_item *pip;
|
|
|
|
if (pamh == NULL)
|
|
return (PAM_SYSTEM_ERR);
|
|
|
|
if (item_type <= 0 || item_type >= PAM_MAX_ITEMS)
|
|
return (PAM_SYMBOL_ERR);
|
|
|
|
pip = (struct pam_item *)&(pamh->ps_item[item_type]);
|
|
|
|
*item = pip->pi_addr;
|
|
|
|
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.
|
|
*/
|
|
|
|
int
|
|
pam_get_user(
|
|
pam_handle_t *pamh, /* PAM handle */
|
|
char **user, /* User Name */
|
|
const char *prompt_override) /* Prompt */
|
|
{
|
|
int status;
|
|
char *prompt = NULL;
|
|
|
|
struct pam_response *ret_resp = (struct pam_response *)0;
|
|
char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
|
|
|
|
if (pamh == NULL)
|
|
return (PAM_SYSTEM_ERR);
|
|
|
|
if ((status = pam_get_item(pamh, PAM_USER, (void **)user))
|
|
!= PAM_SUCCESS) {
|
|
return (status);
|
|
}
|
|
|
|
/* if the user is set, return it */
|
|
|
|
if (*user != NULL && *user[0] != '\0') {
|
|
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 {
|
|
status = pam_get_item(pamh, PAM_USER_PROMPT, (void**)&prompt);
|
|
if (status != PAM_SUCCESS) {
|
|
return (status);
|
|
}
|
|
}
|
|
|
|
/* if the prompt is not set, use default */
|
|
|
|
if (prompt == NULL || prompt[0] == '\0') {
|
|
prompt = PAM_MSG(pamh, 32, "Please enter user name: ");
|
|
}
|
|
|
|
/* prompt for the user */
|
|
|
|
strncpy(messages[0], prompt, sizeof (messages[0]));
|
|
|
|
for (;;) {
|
|
status = __pam_get_input(pamh, PAM_PROMPT_ECHO_ON, 1,
|
|
messages, NULL, &ret_resp);
|
|
|
|
if (status != PAM_SUCCESS) {
|
|
return (status);
|
|
}
|
|
|
|
if (ret_resp->resp && ret_resp->resp[0] != '\0') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* set PAM_USER */
|
|
|
|
status = pam_set_item(pamh, PAM_USER, ret_resp->resp);
|
|
__pam_free_resp(1, ret_resp);
|
|
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.
|
|
*/
|
|
|
|
status = pam_get_item(pamh, PAM_USER, (void**)user);
|
|
return (status);
|
|
}
|
|
|
|
/*
|
|
* Set module specific data
|
|
*/
|
|
pam_set_data(
|
|
pam_handle_t *pamh, /* PAM handle */
|
|
const char *module_data_name, /* unique module data name */
|
|
const void *data, /* the module specific data */
|
|
void (*cleanup)(pam_handle_t *pamh, void *data, int pam_end_status)
|
|
)
|
|
{
|
|
struct pam_module_data *psd;
|
|
|
|
if (pamh == NULL || module_data_name == NULL)
|
|
return (PAM_SYSTEM_ERR);
|
|
|
|
/* check if module data already exists */
|
|
|
|
for (psd = pamh->ssd; psd; psd = psd->next) {
|
|
/*
|
|
* do not free any data because it is not known
|
|
* if the data was originally malloc'd.
|
|
*/
|
|
if (strcmp(psd->module_data_name, module_data_name) == 0) {
|
|
psd->data = (void *)data;
|
|
psd->cleanup = cleanup;
|
|
return (PAM_SUCCESS);
|
|
}
|
|
}
|
|
|
|
psd = malloc(sizeof (struct pam_module_data));
|
|
if (psd == NULL)
|
|
return (PAM_BUF_ERR);
|
|
|
|
psd->module_data_name = strdup(module_data_name);
|
|
if (psd->module_data_name == NULL) {
|
|
free(psd);
|
|
return (PAM_BUF_ERR);
|
|
}
|
|
|
|
psd->data = (void *)data;
|
|
psd->cleanup = cleanup;
|
|
psd->next = pamh->ssd;
|
|
pamh->ssd = psd;
|
|
return (PAM_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* get module specific data
|
|
*/
|
|
|
|
int
|
|
pam_get_data(
|
|
const pam_handle_t *pamh,
|
|
const char *module_data_name,
|
|
void **data
|
|
)
|
|
{
|
|
struct pam_module_data *psd;
|
|
|
|
if (pamh == NULL)
|
|
return (PAM_SYSTEM_ERR);
|
|
|
|
for (psd = pamh->ssd; psd; psd = psd->next) {
|
|
if (strcmp(psd->module_data_name, module_data_name) == 0) {
|
|
*data = psd->data;
|
|
return (PAM_SUCCESS);
|
|
}
|
|
}
|
|
|
|
return (PAM_NO_MODULE_DATA);
|
|
}
|
|
|
|
/*
|
|
* PAM error strings
|
|
*
|
|
* XXX: Make sure these match the errors in pam_appl.h !!!!!!!
|
|
*/
|
|
|
|
static char *pam_error_strings [PAM_TOTAL_ERRNUM] = {
|
|
/* PAM_SUCCESS */ "Success",
|
|
/* PAM_OPEN_ERR */ "Dlopen failure",
|
|
/* PAM_SYMBOL_ERR */ "Symbol not found",
|
|
/* PAM_SERVICE_ERR */ "Error in underlying service module",
|
|
/* PAM_SYSTEM_ERR */ "System error",
|
|
/* PAM_BUF_ERR */ "Memory buffer error",
|
|
/* PAM_CONV_ERR */ "Conversation failure",
|
|
/* PAM_PERM_DENIED */ "Permission denied",
|
|
/* PAM_MAXTRIES */ "Maximum number of attempts exceeded",
|
|
/* PAM_AUTH_ERR */ "Authentication failed",
|
|
/* PAM_AUTHTOKEN_REQD */ "Get new authentication token",
|
|
/* PAM_CRED_INSUFFICIENT */ "Insufficient credentials",
|
|
/* PAM_AUTHINFO_UNAVAIL */ "Can not retrieve authentication info",
|
|
/* PAM_USER_UNKNOWN */ "No account present for user",
|
|
/* PAM_CRED_UNAVAIL */ "Can not retrieve user credentials",
|
|
/* PAM_CRED_EXPIRED */ "User credentials have expired",
|
|
/* PAM_CRED_ERR */ "Failure setting user credentials",
|
|
/* PAM_ACCT_EXPIRED */ "User account has expired",
|
|
/* PAM_AUTHTOK_EXPIRED */ "User password has expired",
|
|
/* PAM_SESSION_ERR */ "Can not make/remove entry for session",
|
|
/* PAM_AUTHTOK_ERR */ "Authentication token manipulation error",
|
|
/* PAM_AUTHTOK_RECOVERY_ERR */ "Authentication token can not be recovered",
|
|
/* PAM_AUTHTOK_LOCK_BUSY */ "Authentication token lock busy",
|
|
/* PAM_AUTHTOK_DISABLE_AGING */ "Authentication token aging disabled",
|
|
/* PAM_NO_MODULE_DATA */ "Module specific data not found",
|
|
/* PAM_IGNORE */ "Ignore module",
|
|
/* PAM_ABORT */ "General PAM failure ",
|
|
/* PAM_TRY_AGAIN */ "Password update failed - Try again "
|
|
};
|
|
|
|
/*
|
|
* PAM equivalent to strerror()
|
|
*/
|
|
char *
|
|
pam_strerror(pam_handle_t *pamh, int errnum)
|
|
{
|
|
if (errnum < 0 || errnum >= PAM_TOTAL_ERRNUM)
|
|
return (PAM_MSG(pamh, 28, "Unknown error"));
|
|
else
|
|
return (PAM_MSG(pamh, errnum, (char *) pam_error_strings[errnum]));
|
|
}
|
|
|
|
/*
|
|
* pam_authenticate - authenticate a user
|
|
*/
|
|
|
|
int
|
|
pam_authenticate(
|
|
pam_handle_t *pamh,
|
|
int flags)
|
|
{
|
|
int error = PAM_AUTH_ERR;
|
|
int first_error = PAM_AUTH_ERR;
|
|
int first_required_error = PAM_AUTH_ERR;
|
|
int required_module_failed = 0;
|
|
int optional_module_failed = 0;
|
|
int success = 0;
|
|
pamtab *modulep;
|
|
struct auth_module *authp;
|
|
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG, "pam_authenticate()");
|
|
|
|
if ((error = load_modules(pamh, PAM_AUTH_MODULE, PAM_SM_AUTHENTICATE))
|
|
!= PAM_SUCCESS) {
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG,
|
|
"pam_authenticate: load_modules failed");
|
|
return (error);
|
|
}
|
|
|
|
modulep = pamh->pam_conf_info[PAM_AUTH_MODULE];
|
|
while (modulep) {
|
|
authp = (struct auth_module *)(modulep->function_ptr);
|
|
if (authp && authp->pam_sm_authenticate) {
|
|
error = authp->pam_sm_authenticate(pamh, flags,
|
|
modulep->module_argc,
|
|
(const char **)modulep->module_argv);
|
|
|
|
switch (error) {
|
|
case PAM_IGNORE:
|
|
/* do nothing */
|
|
break;
|
|
case PAM_SUCCESS:
|
|
if ((modulep->pam_flag & PAM_SUFFICIENT) &&
|
|
!required_module_failed) {
|
|
pam_set_item(pamh,
|
|
PAM_AUTHTOK,
|
|
NULL);
|
|
return (PAM_SUCCESS);
|
|
}
|
|
success = 1;
|
|
break;
|
|
default:
|
|
if (modulep->pam_flag & PAM_REQUIRED) {
|
|
if (!required_module_failed)
|
|
first_required_error = error;
|
|
required_module_failed++;
|
|
} else {
|
|
if (!optional_module_failed)
|
|
first_error = error;
|
|
optional_module_failed++;
|
|
}
|
|
syslog(LOG_DEBUG,
|
|
"pam_authenticate: error %s",
|
|
pam_strerror(pamh, error));
|
|
break;
|
|
}
|
|
}
|
|
modulep = modulep->next;
|
|
}
|
|
|
|
/* this will memset the password memory to 0 */
|
|
pam_set_item(pamh, PAM_AUTHTOK, NULL);
|
|
|
|
if (required_module_failed)
|
|
return (first_required_error);
|
|
else if (success == 0)
|
|
return (first_error);
|
|
else
|
|
return (PAM_SUCCESS);
|
|
|
|
}
|
|
|
|
/*
|
|
* pam_setcred - modify or retrieve user credentials
|
|
*/
|
|
int
|
|
pam_setcred(
|
|
pam_handle_t *pamh,
|
|
int flags)
|
|
{
|
|
int error = PAM_CRED_ERR;
|
|
int first_error = PAM_CRED_ERR;
|
|
int first_required_error = PAM_CRED_ERR;
|
|
int required_module_failed = 0;
|
|
int optional_module_failed = 0;
|
|
int success = 0;
|
|
pamtab *modulep;
|
|
struct auth_module *authp;
|
|
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG, "pam_setcred()");
|
|
|
|
if ((error = load_modules(pamh, PAM_AUTH_MODULE, PAM_SM_SETCRED))
|
|
!= PAM_SUCCESS) {
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG, "pam_setcred: load_modules failed");
|
|
return (error);
|
|
}
|
|
|
|
modulep = pamh->pam_conf_info[PAM_AUTH_MODULE];
|
|
while (modulep) {
|
|
authp = (struct auth_module *)(modulep->function_ptr);
|
|
if (authp && authp->pam_sm_setcred) {
|
|
error = authp->pam_sm_setcred(pamh, flags,
|
|
modulep->module_argc,
|
|
(const char **) modulep->module_argv);
|
|
|
|
switch (error) {
|
|
case PAM_IGNORE:
|
|
/* do nothing */
|
|
break;
|
|
case PAM_SUCCESS:
|
|
/*
|
|
* pam_setcred() should not look at the
|
|
* SUFFICIENT flag because it is only
|
|
* applicable to pam_authenticate().
|
|
*/
|
|
success = 1;
|
|
break;
|
|
default:
|
|
if (modulep->pam_flag & PAM_REQUIRED) {
|
|
if (!required_module_failed)
|
|
first_required_error = error;
|
|
required_module_failed++;
|
|
} else {
|
|
if (!optional_module_failed)
|
|
first_error = error;
|
|
optional_module_failed++;
|
|
}
|
|
syslog(LOG_DEBUG,
|
|
"pam_setcred: error %s",
|
|
pam_strerror(pamh, error));
|
|
break;
|
|
}
|
|
}
|
|
modulep = modulep->next;
|
|
}
|
|
|
|
if (required_module_failed)
|
|
return (first_required_error);
|
|
else if (success == 0)
|
|
return (first_error);
|
|
else
|
|
return (PAM_SUCCESS);
|
|
|
|
}
|
|
|
|
/*
|
|
* pam_acct_mgmt - check password aging, account expiration
|
|
*/
|
|
|
|
int
|
|
pam_acct_mgmt(
|
|
pam_handle_t *pamh,
|
|
int flags)
|
|
{
|
|
int error = PAM_ACCT_EXPIRED;
|
|
int first_error = PAM_ACCT_EXPIRED;
|
|
int first_required_error = PAM_ACCT_EXPIRED;
|
|
int required_module_failed = 0;
|
|
int optional_module_failed = 0;
|
|
int success = 0;
|
|
pamtab *modulep;
|
|
struct account_module *accountp;
|
|
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG, "pam_acct_mgmt()");
|
|
|
|
if ((error = load_modules(pamh, PAM_ACCOUNT_MODULE, PAM_SM_ACCT_MGMT))
|
|
!= PAM_SUCCESS) {
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG,
|
|
"pam_acct_mgmt: load_modules failed");
|
|
return (error);
|
|
}
|
|
|
|
modulep = pamh->pam_conf_info[PAM_ACCOUNT_MODULE];
|
|
while (modulep) {
|
|
accountp = (struct account_module *)(modulep->function_ptr);
|
|
if (accountp && accountp->pam_sm_acct_mgmt) {
|
|
error = accountp->pam_sm_acct_mgmt(pamh, flags,
|
|
modulep->module_argc,
|
|
(const char **) modulep->module_argv);
|
|
|
|
switch (error) {
|
|
case PAM_IGNORE:
|
|
/* do nothing */
|
|
break;
|
|
case PAM_SUCCESS:
|
|
if ((modulep->pam_flag & PAM_SUFFICIENT) &&
|
|
!required_module_failed)
|
|
return (PAM_SUCCESS);
|
|
success = 1;
|
|
break;
|
|
default:
|
|
if (modulep->pam_flag & PAM_REQUIRED) {
|
|
if (!required_module_failed)
|
|
first_required_error = error;
|
|
required_module_failed++;
|
|
} else {
|
|
if (!optional_module_failed)
|
|
first_error = error;
|
|
optional_module_failed++;
|
|
}
|
|
syslog(LOG_DEBUG,
|
|
"pam_acct_mgmt: error %s",
|
|
pam_strerror(pamh, error));
|
|
break;
|
|
}
|
|
}
|
|
modulep = modulep->next;
|
|
}
|
|
|
|
if (required_module_failed)
|
|
return (first_required_error);
|
|
else if (success == 0)
|
|
return (first_error);
|
|
else
|
|
return (PAM_SUCCESS);
|
|
|
|
}
|
|
|
|
/*
|
|
* pam_open_session - begin session management
|
|
*/
|
|
|
|
int
|
|
pam_open_session(
|
|
pam_handle_t *pamh,
|
|
int flags)
|
|
{
|
|
int error = PAM_SESSION_ERR;
|
|
int first_error = PAM_SESSION_ERR;
|
|
int first_required_error = PAM_SESSION_ERR;
|
|
int required_module_failed = 0;
|
|
int optional_module_failed = 0;
|
|
int success = 0;
|
|
pamtab *modulep;
|
|
struct session_module *sessionp;
|
|
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG, "pam_open_session()");
|
|
|
|
if ((error = load_modules(pamh, PAM_SESSION_MODULE,
|
|
PAM_SM_OPEN_SESSION)) != PAM_SUCCESS) {
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG,
|
|
"pam_open_session: load_modules failed");
|
|
return (error);
|
|
}
|
|
|
|
modulep = pamh->pam_conf_info[PAM_SESSION_MODULE];
|
|
while (modulep) {
|
|
sessionp = (struct session_module *)(modulep->function_ptr);
|
|
if (sessionp && sessionp->pam_sm_open_session) {
|
|
error = sessionp->pam_sm_open_session(pamh, flags,
|
|
modulep->module_argc,
|
|
(const char **) modulep->module_argv);
|
|
|
|
switch (error) {
|
|
case PAM_IGNORE:
|
|
/* do nothing */
|
|
break;
|
|
case PAM_SUCCESS:
|
|
if ((modulep->pam_flag & PAM_SUFFICIENT) &&
|
|
!required_module_failed)
|
|
return (PAM_SUCCESS);
|
|
success = 1;
|
|
break;
|
|
default:
|
|
if (modulep->pam_flag & PAM_REQUIRED) {
|
|
if (!required_module_failed)
|
|
first_required_error = error;
|
|
required_module_failed++;
|
|
} else {
|
|
if (!optional_module_failed)
|
|
first_error = error;
|
|
optional_module_failed++;
|
|
}
|
|
syslog(LOG_DEBUG,
|
|
"pam_open_session: error %s",
|
|
pam_strerror(pamh, error));
|
|
break;
|
|
}
|
|
}
|
|
modulep = modulep->next;
|
|
}
|
|
|
|
if (required_module_failed)
|
|
return (first_required_error);
|
|
else if (success == 0)
|
|
return (first_error);
|
|
else
|
|
return (PAM_SUCCESS);
|
|
|
|
}
|
|
|
|
/*
|
|
* pam_close_session - terminate session management
|
|
*/
|
|
|
|
int
|
|
pam_close_session(
|
|
pam_handle_t *pamh,
|
|
int flags)
|
|
{
|
|
int error = PAM_SESSION_ERR;
|
|
int first_error = PAM_SESSION_ERR;
|
|
int first_required_error = PAM_SESSION_ERR;
|
|
int required_module_failed = 0;
|
|
int optional_module_failed = 0;
|
|
int success = 0;
|
|
pamtab *modulep;
|
|
struct session_module *sessionp;
|
|
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG, "pam_close_session()");
|
|
|
|
if ((error = load_modules(pamh, PAM_SESSION_MODULE,
|
|
PAM_SM_CLOSE_SESSION)) != PAM_SUCCESS) {
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG,
|
|
"pam_close_session: load_modules failed");
|
|
return (error);
|
|
}
|
|
|
|
modulep = pamh->pam_conf_info[PAM_SESSION_MODULE];
|
|
while (modulep) {
|
|
sessionp = (struct session_module *)(modulep->function_ptr);
|
|
if (sessionp && sessionp->pam_sm_close_session) {
|
|
error = sessionp->pam_sm_close_session(
|
|
pamh, flags, modulep->module_argc,
|
|
(const char **) modulep->module_argv);
|
|
|
|
switch (error) {
|
|
case PAM_IGNORE:
|
|
/* do nothing */
|
|
break;
|
|
case PAM_SUCCESS:
|
|
if ((modulep->pam_flag & PAM_SUFFICIENT) &&
|
|
!required_module_failed)
|
|
return (PAM_SUCCESS);
|
|
success = 1;
|
|
break;
|
|
default:
|
|
if (modulep->pam_flag & PAM_REQUIRED) {
|
|
if (!required_module_failed)
|
|
first_required_error = error;
|
|
required_module_failed++;
|
|
} else {
|
|
if (!optional_module_failed)
|
|
first_error = error;
|
|
optional_module_failed++;
|
|
}
|
|
syslog(LOG_DEBUG,
|
|
"pam_close_session: error %s",
|
|
pam_strerror(pamh, error));
|
|
break;
|
|
}
|
|
}
|
|
modulep = modulep->next;
|
|
}
|
|
|
|
if (required_module_failed)
|
|
return (first_required_error);
|
|
else if (success == 0)
|
|
return (first_error);
|
|
else
|
|
return (PAM_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* pam_chauthtok - change user authentication token
|
|
*/
|
|
|
|
int
|
|
pam_chauthtok(
|
|
pam_handle_t *pamh,
|
|
int flags)
|
|
{
|
|
int error = PAM_AUTHTOK_ERR;
|
|
int first_error = PAM_AUTHTOK_ERR;
|
|
int first_required_error = PAM_AUTHTOK_ERR;
|
|
int required_module_failed = 0;
|
|
int optional_module_failed = 0;
|
|
int success = 0;
|
|
int i;
|
|
int sm_flags;
|
|
pamtab *modulep;
|
|
struct password_module *passwdp;
|
|
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG, "pam_chauthtok()");
|
|
|
|
/* do not let apps use PAM_PRELIM_CHECK or PAM_UPDATE_AUTHTOK */
|
|
if (flags & PAM_PRELIM_CHECK || flags & PAM_UPDATE_AUTHTOK)
|
|
return (PAM_SYMBOL_ERR);
|
|
|
|
if ((error = load_modules(pamh, PAM_PASSWORD_MODULE, PAM_SM_CHAUTHTOK))
|
|
!= PAM_SUCCESS) {
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG,
|
|
"pam_chauthtok: load_modules failed");
|
|
return (error);
|
|
}
|
|
|
|
for (i = 1; i <= 2; i++) {
|
|
switch (i) {
|
|
case 1:
|
|
/* first time thru loop do preliminary check */
|
|
sm_flags = flags | PAM_PRELIM_CHECK;
|
|
break;
|
|
case 2:
|
|
/* 2nd time thru loop update passwords */
|
|
success = 0;
|
|
sm_flags = flags | PAM_UPDATE_AUTHTOK;
|
|
break;
|
|
}
|
|
|
|
modulep = pamh->pam_conf_info[PAM_PASSWORD_MODULE];
|
|
while (modulep) {
|
|
passwdp = (struct password_module *)(modulep->function_ptr);
|
|
if (passwdp && passwdp->pam_sm_chauthtok) {
|
|
error = passwdp->pam_sm_chauthtok(pamh, sm_flags,
|
|
modulep->module_argc,
|
|
(const char **) modulep->module_argv);
|
|
|
|
switch (error) {
|
|
case PAM_IGNORE:
|
|
/* do nothing */
|
|
break;
|
|
case PAM_SUCCESS:
|
|
if (modulep->pam_flag & PAM_SUFFICIENT) {
|
|
if (!(flags & PAM_PRELIM_CHECK) &&
|
|
!required_module_failed) {
|
|
/*
|
|
* If updating passwords, the
|
|
* module is sufficient, and
|
|
* no required modules have
|
|
* failed, then memset the
|
|
* passwd memory to 0 and return
|
|
*/
|
|
pam_set_item(pamh,
|
|
PAM_AUTHTOK, NULL);
|
|
pam_set_item(pamh,
|
|
PAM_OLDAUTHTOK, NULL);
|
|
return (PAM_SUCCESS);
|
|
}
|
|
}
|
|
success = 1;
|
|
break;
|
|
default:
|
|
if (modulep->pam_flag & PAM_REQUIRED) {
|
|
if (!required_module_failed)
|
|
first_required_error = error;
|
|
required_module_failed++;
|
|
} else {
|
|
if (!optional_module_failed)
|
|
first_error = error;
|
|
optional_module_failed++;
|
|
}
|
|
syslog(LOG_DEBUG,
|
|
"pam_chauthtok: error %s",
|
|
pam_strerror(pamh, error));
|
|
break;
|
|
}
|
|
}
|
|
modulep = modulep->next;
|
|
} /* while */
|
|
|
|
/* this will memset the password memory to 0 */
|
|
pam_set_item(pamh, PAM_AUTHTOK, NULL);
|
|
pam_set_item(pamh, PAM_OLDAUTHTOK, NULL);
|
|
|
|
if (required_module_failed)
|
|
return (first_required_error);
|
|
else if (success == 0)
|
|
return (first_error);
|
|
else if (sm_flags & PAM_UPDATE_AUTHTOK) {
|
|
/*
|
|
* Only return PAM_SUCCESS if this is the second
|
|
* time through the loop.
|
|
*/
|
|
return (PAM_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* If we reach here, the prelim check succeeded.
|
|
* Go thru the loop again to update the passwords.
|
|
*/
|
|
|
|
} /* for */
|
|
|
|
/* should never reach this point!!! */
|
|
}
|
|
|
|
/*
|
|
* load_modules()
|
|
* open_module()
|
|
* load_function()
|
|
*
|
|
* 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
|
|
load_modules(pam_handle_t *pamh, int type, char *function_name)
|
|
{
|
|
|
|
#ifdef sun
|
|
void *mh;
|
|
#endif
|
|
|
|
#ifdef hpV4
|
|
shl_t mh;
|
|
#endif
|
|
|
|
pamtab *pam_entry;
|
|
struct auth_module *authp;
|
|
struct account_module *accountp;
|
|
struct session_module *sessionp;
|
|
struct password_module *passwdp;
|
|
int loading_functions = 0; /* are we currently loading functions? */
|
|
|
|
if ((pam_entry = pamh->pam_conf_info[type]) == NULL) {
|
|
syslog(LOG_ERR, "load_modules: no module present");
|
|
return (PAM_SYSTEM_ERR);
|
|
}
|
|
|
|
while (pam_entry != NULL) {
|
|
if (pam_debug)
|
|
syslog(LOG_DEBUG, "load_modules: %s",
|
|
pam_entry->module_path);
|
|
|
|
switch (type) {
|
|
case PAM_AUTH_MODULE:
|
|
|
|
/* if the function has already been loaded, return */
|
|
authp = pam_entry->function_ptr;
|
|
if (!loading_functions &&
|
|
(((strcmp(function_name, PAM_SM_AUTHENTICATE)
|
|
== 0) &&
|
|
authp && authp->pam_sm_authenticate) ||
|
|
((strcmp(function_name, PAM_SM_SETCRED) == 0) &&
|
|
authp && authp->pam_sm_setcred))) {
|
|
return (PAM_SUCCESS);
|
|
}
|
|
|
|
/* function has not been loaded yet */
|
|
loading_functions = 1;
|
|
authp = (struct auth_module *)
|
|
calloc(1, sizeof (struct auth_module));
|
|
if (authp == NULL)
|
|
return (PAM_BUF_ERR);
|
|
|
|
/* if open_module fails, continue */
|
|
if ((mh = open_module
|
|
(pam_entry->module_path)) == NULL)
|
|
syslog(LOG_ERR,
|
|
"load_modules: can not open module %s",
|
|
pam_entry->module_path);
|
|
|
|
/* load the authentication function */
|
|
if (strcmp(function_name, PAM_SM_AUTHENTICATE) == 0) {
|
|
if (load_function(mh, PAM_SM_AUTHENTICATE,
|
|
&authp->pam_sm_authenticate) !=
|
|
PAM_SUCCESS) {
|
|
/* ignore if dlsym fails */
|
|
free(authp);
|
|
break;
|
|
}
|
|
|
|
/* load the setcred function */
|
|
} else if (strcmp(function_name, PAM_SM_SETCRED) == 0) {
|
|
if (load_function(mh, PAM_SM_SETCRED,
|
|
&authp->pam_sm_setcred) != PAM_SUCCESS) {
|
|
/* ignore if dlsym fails */
|
|
free(authp);
|
|
break;
|
|
}
|
|
}
|
|
pam_entry->function_ptr = authp;
|
|
break;
|
|
case PAM_ACCOUNT_MODULE:
|
|
accountp = pam_entry->function_ptr;
|
|
if (!loading_functions &&
|
|
(strcmp(function_name, PAM_SM_ACCT_MGMT) == 0) &&
|
|
accountp && accountp->pam_sm_acct_mgmt) {
|
|
return (PAM_SUCCESS);
|
|
}
|
|
|
|
loading_functions = 1;
|
|
accountp = (struct account_module *)
|
|
calloc(1, sizeof (struct account_module));
|
|
if (accountp == NULL)
|
|
return (PAM_BUF_ERR);
|
|
|
|
/* if open_module fails, continue */
|
|
if ((mh = open_module
|
|
(pam_entry->module_path)) == NULL)
|
|
syslog(LOG_ERR,
|
|
"load_modules: can not open module %s",
|
|
pam_entry->module_path);
|
|
|
|
if (load_function(mh, PAM_SM_ACCT_MGMT,
|
|
&accountp->pam_sm_acct_mgmt)
|
|
!= PAM_SUCCESS) {
|
|
syslog(LOG_ERR,
|
|
"load_modules: pam_sm_acct_mgmt() missing");
|
|
free(accountp);
|
|
break;
|
|
}
|
|
pam_entry->function_ptr = accountp;
|
|
break;
|
|
case PAM_SESSION_MODULE:
|
|
sessionp = pam_entry->function_ptr;
|
|
if (!loading_functions &&
|
|
(((strcmp(function_name, PAM_SM_OPEN_SESSION)
|
|
== 0) &&
|
|
sessionp && sessionp->pam_sm_open_session) ||
|
|
((strcmp(function_name, PAM_SM_CLOSE_SESSION)
|
|
== 0) &&
|
|
sessionp && sessionp->pam_sm_close_session))) {
|
|
return (PAM_SUCCESS);
|
|
}
|
|
|
|
loading_functions = 1;
|
|
sessionp = (struct session_module *)
|
|
calloc(1, sizeof (struct session_module));
|
|
if (sessionp == NULL)
|
|
return (PAM_BUF_ERR);
|
|
|
|
/* if open_module fails, continue */
|
|
if ((mh = open_module
|
|
(pam_entry->module_path)) == NULL)
|
|
syslog(LOG_ERR,
|
|
"load_modules: can not open module %s",
|
|
pam_entry->module_path);
|
|
|
|
if ((strcmp(function_name, PAM_SM_OPEN_SESSION) == 0) &&
|
|
load_function(mh, PAM_SM_OPEN_SESSION,
|
|
&sessionp->pam_sm_open_session)
|
|
!= PAM_SUCCESS) {
|
|
free(sessionp);
|
|
break;
|
|
} else if ((strcmp(function_name,
|
|
PAM_SM_CLOSE_SESSION) == 0) &&
|
|
load_function(mh, PAM_SM_CLOSE_SESSION,
|
|
&sessionp->pam_sm_close_session)
|
|
!= PAM_SUCCESS) {
|
|
free(sessionp);
|
|
break;
|
|
}
|
|
pam_entry->function_ptr = sessionp;
|
|
break;
|
|
case PAM_PASSWORD_MODULE:
|
|
passwdp = pam_entry->function_ptr;
|
|
if (!loading_functions &&
|
|
(strcmp(function_name, PAM_SM_CHAUTHTOK) == 0) &&
|
|
passwdp && passwdp->pam_sm_chauthtok) {
|
|
return (PAM_SUCCESS);
|
|
}
|
|
|
|
loading_functions = 1;
|
|
passwdp = (struct password_module *)
|
|
calloc(1, sizeof (struct password_module));
|
|
if (passwdp == NULL)
|
|
return (PAM_BUF_ERR);
|
|
|
|
/* if open_module fails, continue */
|
|
if ((mh = open_module
|
|
(pam_entry->module_path)) == NULL)
|
|
syslog(LOG_ERR,
|
|
"load_modules: can not open module %s",
|
|
pam_entry->module_path);
|
|
|
|
if (load_function(mh, PAM_SM_CHAUTHTOK,
|
|
&passwdp->pam_sm_chauthtok) != PAM_SUCCESS) {
|
|
free(passwdp);
|
|
break;
|
|
}
|
|
pam_entry->function_ptr = passwdp;
|
|
break;
|
|
default:
|
|
if (pam_debug) {
|
|
syslog(LOG_DEBUG,
|
|
"load_modules: unsupported type %d", type);
|
|
}
|
|
break;
|
|
}
|
|
|
|
pam_entry = pam_entry->next;
|
|
} /* while */
|
|
|
|
return (PAM_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* open_module - Open the module first checking for
|
|
* propers modes and ownerships on the file.
|
|
*/
|
|
|
|
#ifdef sun
|
|
static void *
|
|
open_module(char *module_so)
|
|
{
|
|
#endif
|
|
#ifdef hpV4
|
|
static shl_t
|
|
open_module(char *module_so)
|
|
{
|
|
#endif
|
|
struct stat stb;
|
|
char *errmsg;
|
|
#ifdef sun
|
|
void *lfd;
|
|
#endif /* sun */
|
|
#ifdef hpV4
|
|
shl_t lfd;
|
|
#endif /* hpV4 */
|
|
|
|
/*
|
|
* Stat the file so we can check modes and ownerships
|
|
*/
|
|
if (stat(module_so, &stb) < 0) {
|
|
syslog(LOG_ALERT, "open_module: stat(%s) failed: %s",
|
|
module_so, strerror(errno));
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Check the ownership of the file
|
|
*/
|
|
if (stb.st_uid != (uid_t)0) {
|
|
syslog(LOG_ALERT,
|
|
"open_module: Owner of the module %s is not root",
|
|
module_so);
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Check the modes on the file
|
|
*/
|
|
if (stb.st_mode&S_IWGRP) {
|
|
syslog(LOG_ALERT,
|
|
"open_module: module %s writable by group",
|
|
module_so);
|
|
return (NULL);
|
|
}
|
|
if (stb.st_mode&S_IWOTH) {
|
|
syslog(LOG_ALERT,
|
|
"open_module: module %s writable by world", module_so);
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Perform the dlopen()
|
|
*/
|
|
#ifdef sun
|
|
lfd = (void *) dlopen(module_so, RTLD_LAZY);
|
|
#endif /* sun */
|
|
|
|
#ifdef hpV4
|
|
lfd = shl_load(module_so, BIND_DEFERRED, 0L);
|
|
#endif /* hpV4 */
|
|
|
|
if (lfd == NULL) {
|
|
if (pam_debug) {
|
|
errmsg = (char *) strerror(errno);
|
|
syslog(LOG_DEBUG, "open_module: %s failed: %s",
|
|
module_so, errmsg);
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
return (lfd);
|
|
|
|
}
|
|
|
|
/*
|
|
* load_function - call dlsym() to resolve the function address
|
|
*/
|
|
#ifdef sun
|
|
static int
|
|
load_function(void *lfd, char *name, int (**func)())
|
|
{
|
|
#endif
|
|
#ifdef hpV4
|
|
static int
|
|
load_function(shl_t lfd, char *name, int (**func)())
|
|
{
|
|
#endif
|
|
char *errmsg = NULL;
|
|
|
|
#ifdef hpV4
|
|
void *proc_addr = NULL;
|
|
int stat;
|
|
|
|
#endif
|
|
|
|
if (lfd == NULL)
|
|
return (PAM_SYMBOL_ERR);
|
|
|
|
/*
|
|
* The APIs for opening the shared objects are palatform dependent
|
|
* and hence the ifdef platform
|
|
*/
|
|
#ifdef sun
|
|
*func = (int (*)())dlsym(lfd, name);
|
|
if (*func == NULL) {
|
|
if (pam_debug) {
|
|
errmsg = (char *) dlerror();
|
|
syslog(LOG_DEBUG,
|
|
"dlsym failed %s: error %s",
|
|
name, errmsg != NULL ? errmsg : "");
|
|
}
|
|
return (PAM_SYMBOL_ERR);
|
|
}
|
|
#endif
|
|
|
|
#ifdef hpV4
|
|
|
|
stat = shl_findsym(&lfd, name, TYPE_PROCEDURE, proc_addr);
|
|
|
|
*func = (int (*)())proc_addr;
|
|
|
|
if (stat) {
|
|
if (pam_debug) {
|
|
strerror_r(errno, errmsg, MAX_ERRMESSAGE_LENGTH);
|
|
syslog(LOG_DEBUG, "shl_findsym failed %s: error %s",
|
|
name, errmsg != NULL ? errmsg : "");
|
|
}
|
|
return (PAM_SYMBOL_ERR);
|
|
}
|
|
#endif
|
|
if (pam_debug) {
|
|
syslog(LOG_DEBUG,
|
|
"load_function: successful load of %s", name);
|
|
}
|
|
return (PAM_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* open_pam_conf()
|
|
* close_pam_conf()
|
|
* read_pam_conf()
|
|
* get_pam_conf_entry()
|
|
* read_next_token()
|
|
* nextline()
|
|
*
|
|
* Routines to read the pam.conf configuration file
|
|
*/
|
|
|
|
/*
|
|
* open_pam_conf - open the pam.conf config file
|
|
*/
|
|
|
|
static int
|
|
open_pam_conf(struct pam_fh **pam_fh)
|
|
{
|
|
struct stat stb;
|
|
|
|
/*
|
|
* Stat the file so we can check modes and ownerships
|
|
*/
|
|
if (stat(PAM_CONFIG, &stb) < 0) {
|
|
syslog(LOG_ALERT, "open_pam_conf: stat(%s) failed: %s",
|
|
PAM_CONFIG, strerror(errno));
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Check the ownership of the file
|
|
*/
|
|
if (stb.st_uid != (uid_t)0) {
|
|
syslog(LOG_ALERT,
|
|
"open_pam_conf: Owner of %s is not root", PAM_CONFIG);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Check the modes on the file
|
|
*/
|
|
if (stb.st_mode&S_IWGRP) {
|
|
syslog(LOG_ALERT,
|
|
"open_pam_conf: %s writable by group", PAM_CONFIG);
|
|
return (0);
|
|
}
|
|
if (stb.st_mode&S_IWOTH) {
|
|
syslog(LOG_ALERT,
|
|
"open_pam_conf: %s writable by world", PAM_CONFIG);
|
|
return (0);
|
|
}
|
|
|
|
*pam_fh = calloc(1, sizeof (struct pam_fh));
|
|
if (*pam_fh == NULL)
|
|
return (0);
|
|
|
|
(*pam_fh)->fconfig = fopen(PAM_CONFIG, "r");
|
|
return ((*pam_fh)->fconfig != NULL);
|
|
}
|
|
|
|
/*
|
|
* close_pam_conf - close pam.conf
|
|
*/
|
|
|
|
static void
|
|
close_pam_conf(struct pam_fh *pam_fh)
|
|
{
|
|
|
|
if (pam_fh->fconfig == NULL) {
|
|
free(pam_fh);
|
|
return;
|
|
}
|
|
fclose(pam_fh->fconfig);
|
|
free(pam_fh);
|
|
}
|
|
|
|
/*
|
|
* read_pam_conf - read in each entry in pam.conf and store info
|
|
* under the pam handle.
|
|
*/
|
|
|
|
static int
|
|
read_pam_conf(pam_handle_t *pamh)
|
|
{
|
|
|
|
struct pam_fh *pam_fh = NULL;
|
|
pamtab *pamentp = NULL;
|
|
pamtab *pament_traverse = NULL;
|
|
pamtab *new_head = NULL;
|
|
char *service;
|
|
int other_needed[PAM_NUM_MODULE_TYPES] = {1, 1, 1, 1};
|
|
int error;
|
|
|
|
if ((error = pam_get_item(pamh, PAM_SERVICE, (void **)&service))
|
|
!= PAM_SUCCESS)
|
|
goto out;
|
|
|
|
if (open_pam_conf(&pam_fh) == 0) {
|
|
error = PAM_SYSTEM_ERR;
|
|
goto out;
|
|
}
|
|
|
|
while (get_pam_conf_entry(pam_fh, &pamentp)) {
|
|
/*
|
|
* Look for the current service.
|
|
* If we find it, purge all "other" services
|
|
* that we have stored. Keep loading "other"
|
|
* services if we still haven't found an entry
|
|
* for the current service.
|
|
*/
|
|
if (other_needed[pamentp->pam_type] == 1 &&
|
|
!strcasecmp(pamentp->pam_service, service)) {
|
|
|
|
/*
|
|
* We found a service match. Purge the "other"
|
|
* entries we have stored.
|
|
*/
|
|
new_head = pamh->pam_conf_info[pamentp->pam_type];
|
|
pament_traverse = new_head;
|
|
while (new_head != NULL &&
|
|
!strcasecmp(new_head->pam_service, "other")) {
|
|
new_head = new_head->next;
|
|
free_pamconf(pament_traverse);
|
|
pament_traverse = new_head;
|
|
}
|
|
pamh->pam_conf_info[pamentp->pam_type] = new_head;
|
|
|
|
/* "other" entries no longer needed for this type */
|
|
other_needed[pamentp->pam_type] = 0;
|
|
}
|
|
|
|
/* add service */
|
|
if ((other_needed[pamentp->pam_type] == 1 &&
|
|
!strcasecmp(pamentp->pam_service, "other")) ||
|
|
(other_needed[pamentp->pam_type] == 0 &&
|
|
!strcasecmp(pamentp->pam_service, service))) {
|
|
|
|
pamentp->next = NULL;
|
|
if (pamh->pam_conf_info[pamentp->pam_type] == NULL) {
|
|
pamh->pam_conf_info[pamentp->pam_type] =
|
|
pamentp;
|
|
} else {
|
|
pament_traverse =
|
|
pamh->pam_conf_info[pamentp->pam_type];
|
|
while (pament_traverse->next != NULL)
|
|
pament_traverse = pament_traverse->next;
|
|
pament_traverse->next = pamentp;
|
|
}
|
|
} else
|
|
free_pamconf(pamentp);
|
|
|
|
pamentp = NULL;
|
|
}
|
|
|
|
(void) close_pam_conf(pam_fh);
|
|
error = PAM_SUCCESS;
|
|
out:
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* get_pam_conf_entry - get a pam.conf entry
|
|
* return 0: no more entries or error
|
|
* return non 0: good entry
|
|
* all bad entries in pam.conf are ignored.
|
|
*/
|
|
|
|
static int
|
|
get_pam_conf_entry(struct pam_fh *pam_fh, pamtab **pam)
|
|
{
|
|
char *cp, *arg;
|
|
int argc;
|
|
char *tmp, *tmp_free;
|
|
int i;
|
|
|
|
while (1) {
|
|
if ((*pam = (pamtab *)calloc(1, sizeof (pamtab))) == NULL)
|
|
return (0);
|
|
|
|
cp = nextline(pam_fh);
|
|
if (cp == (char *) 0)
|
|
goto mal_formed;
|
|
|
|
arg = read_next_token(&cp);
|
|
if (arg == NULL) {
|
|
syslog(LOG_CRIT,
|
|
"illegal entry in pam.conf: missing field: %s", cp);
|
|
free_pamconf (*pam);
|
|
continue;
|
|
}
|
|
|
|
(*pam)->pam_service = strdup(arg);
|
|
if ((*pam)->pam_service == NULL) {
|
|
syslog(LOG_ERR, "strdup: out of memory");
|
|
goto mal_formed;
|
|
}
|
|
|
|
if (cp == (char *) 0) {
|
|
syslog(LOG_CRIT,
|
|
"illegal entry in pam.conf: missing field: %s", cp);
|
|
free_pamconf (*pam);
|
|
continue;
|
|
}
|
|
|
|
arg = read_next_token(&cp);
|
|
if (cp == (char *) 0) {
|
|
syslog(LOG_CRIT,
|
|
"illegal entry in pam.conf: missing field: %s", cp);
|
|
free_pamconf (*pam);
|
|
continue;
|
|
}
|
|
if (strcasecmp(arg, "auth") == 0)
|
|
(*pam)->pam_type = PAM_AUTH_MODULE;
|
|
else if (strcasecmp(arg, "account") == 0)
|
|
(*pam)->pam_type = PAM_ACCOUNT_MODULE;
|
|
else if (strcasecmp(arg, "session") == 0)
|
|
(*pam)->pam_type = PAM_SESSION_MODULE;
|
|
else if (strcasecmp(arg, "password") == 0)
|
|
(*pam)->pam_type = PAM_PASSWORD_MODULE;
|
|
else {
|
|
/* error */
|
|
syslog(LOG_CRIT, "invalid module type: %s", arg);
|
|
free_pamconf (*pam);
|
|
continue;
|
|
}
|
|
|
|
/* pam level flag */
|
|
arg = read_next_token(&cp);
|
|
if (cp == (char *) 0) {
|
|
syslog(LOG_CRIT,
|
|
"illegal entry in pam.conf: missing field: %s", cp);
|
|
free_pamconf (*pam);
|
|
continue;
|
|
}
|
|
if (strcasecmp(arg, "required") == 0)
|
|
(*pam)->pam_flag = PAM_REQUIRED;
|
|
else if (strcasecmp(arg, "optional") == 0)
|
|
(*pam)->pam_flag = PAM_OPTIONAL;
|
|
else if (strcasecmp(arg, "sufficient") == 0)
|
|
(*pam)->pam_flag = PAM_SUFFICIENT;
|
|
else {
|
|
/* error */
|
|
syslog(LOG_CRIT, "invalid flag: %s", arg);
|
|
free_pamconf (*pam);
|
|
continue;
|
|
}
|
|
|
|
arg = read_next_token(&cp);
|
|
if (cp == (char *) 0) {
|
|
syslog(LOG_CRIT,
|
|
"illegal entry in pam.conf: missing field: %s", cp);
|
|
free_pamconf (*pam);
|
|
continue;
|
|
}
|
|
/*
|
|
* If module path does not start with "/", then
|
|
* prepend PAM_LIB_DIR (/usr/lib/security/).
|
|
*/
|
|
if (arg[0] != '/') {
|
|
if (((*pam)->module_path = (char *)
|
|
calloc(strlen(PAM_LIB_DIR) + strlen(arg) + 1,
|
|
sizeof (char))) == NULL) {
|
|
syslog(LOG_ERR, "strdup: out of memory");
|
|
goto mal_formed;
|
|
}
|
|
sprintf((*pam)->module_path, "%s%s",
|
|
PAM_LIB_DIR, arg);
|
|
} else {
|
|
(*pam)->module_path = strdup(arg);
|
|
if ((*pam)->module_path == NULL) {
|
|
syslog(LOG_ERR, "strdup: out of memory");
|
|
goto mal_formed;
|
|
}
|
|
}
|
|
|
|
/* count the number of options first */
|
|
argc = 0;
|
|
if ((tmp = strdup(cp)) == NULL) {
|
|
syslog(LOG_ERR, "strdup: out of memory");
|
|
goto mal_formed;
|
|
}
|
|
tmp_free = tmp;
|
|
for (arg = read_next_token(&tmp); arg;
|
|
arg = read_next_token(&tmp))
|
|
argc++;
|
|
free(tmp_free);
|
|
|
|
/* allocate pointer array */
|
|
if (argc > 0) {
|
|
(*pam)->module_argv = (char **)
|
|
calloc(argc+1, sizeof (char *));
|
|
if ((*pam)->module_argv == NULL) {
|
|
syslog(LOG_ERR, "calloc: out of memory");
|
|
goto mal_formed;
|
|
}
|
|
i = 0;
|
|
for (arg = read_next_token(&cp); arg;
|
|
arg = read_next_token(&cp)) {
|
|
(*pam)->module_argv[i] = strdup(arg);
|
|
if ((*pam)->module_argv[i] == NULL) {
|
|
syslog(LOG_ERR, "strdup failed");
|
|
goto mal_formed;
|
|
}
|
|
i++;
|
|
}
|
|
(*pam)->module_argv[argc] = NULL;
|
|
}
|
|
(*pam)->module_argc = argc;
|
|
break;
|
|
} /* while */
|
|
|
|
return (1);
|
|
|
|
mal_formed:
|
|
|
|
free_pamconf(*pam);
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* read_next_token - skip tab and space characters and return the next token
|
|
*/
|
|
|
|
static char *
|
|
read_next_token(char **cpp)
|
|
{
|
|
register char *cp = *cpp;
|
|
char *start;
|
|
|
|
while (*cp == ' ' || *cp == '\t')
|
|
cp++;
|
|
if (*cp == '\0') {
|
|
*cpp = (char *)0;
|
|
return ((char *)0);
|
|
}
|
|
start = cp;
|
|
while (*cp && *cp != ' ' && *cp != '\t')
|
|
cp++;
|
|
if (*cp != '\0')
|
|
*cp++ = '\0';
|
|
*cpp = cp;
|
|
return (start);
|
|
}
|
|
|
|
/*
|
|
* nextline - skip all blank lines and comments
|
|
*/
|
|
|
|
static char *
|
|
nextline(struct pam_fh *pam_fh)
|
|
{
|
|
char *cp;
|
|
char *ll;
|
|
|
|
/*
|
|
* Skip the blank line, comment line
|
|
*/
|
|
while ((ll = fgets(pam_fh->line, sizeof (pam_fh->line),
|
|
pam_fh->fconfig)) && (*(pam_fh->line) == '\n' ||
|
|
*(pam_fh->line) == '#'));
|
|
|
|
if (ll == NULL)
|
|
return ((char *)0);
|
|
cp = strchr(pam_fh->line, '\n');
|
|
if (cp)
|
|
*cp = '\0';
|
|
return (pam_fh->line);
|
|
}
|
|
|
|
/*
|
|
* clean_up()
|
|
* free_pamconf()
|
|
* free_pam_conf_info()
|
|
*
|
|
* Routines to free allocated storage
|
|
*/
|
|
|
|
/*
|
|
* clean_up - free allocated storage in the pam handle
|
|
*/
|
|
|
|
static void
|
|
clean_up(pam_handle_t *pamh)
|
|
{
|
|
int i;
|
|
|
|
if (pamh) {
|
|
free_pam_conf_info(pamh);
|
|
for (i = 0; i < PAM_MAX_ITEMS; i++) {
|
|
if (pamh->ps_item[i].pi_addr != NULL)
|
|
free(pamh->ps_item[i].pi_addr);
|
|
}
|
|
free(pamh);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* free_pamconf - free memory used to store pam.conf entry
|
|
*/
|
|
|
|
static void
|
|
free_pamconf(pamtab *cp)
|
|
{
|
|
int i;
|
|
|
|
if (cp) {
|
|
if (cp->pam_service)
|
|
free(cp->pam_service);
|
|
if (cp->module_path)
|
|
free(cp->module_path);
|
|
for (i = 0; i < cp->module_argc; i++) {
|
|
if (cp->module_argv[i])
|
|
free(cp->module_argv[i]);
|
|
}
|
|
if (cp->module_argc > 0)
|
|
free(cp->module_argv);
|
|
if (cp->function_ptr)
|
|
free(cp->function_ptr);
|
|
|
|
free(cp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* free_pam_conf_info - free memory used to store all pam.conf info
|
|
* under the pam handle
|
|
*/
|
|
|
|
static void
|
|
free_pam_conf_info(pam_handle_t *pamh)
|
|
{
|
|
|
|
pamtab *pamentp;
|
|
pamtab *pament_trail;
|
|
int i;
|
|
|
|
for (i = 0; i < PAM_NUM_MODULE_TYPES; i++) {
|
|
pamentp = pamh->pam_conf_info[i];
|
|
pament_trail = pamentp;
|
|
while (pamentp) {
|
|
pamentp = pamentp->next;
|
|
free_pamconf(pament_trail);
|
|
pament_trail = pamentp;
|
|
}
|
|
}
|
|
|
|
}
|