Add options to capture stdout and / or stderr and pass the output on
to the user. There is currently no buffering, so the result may be somewhat unpredictable if the conversation function adds a newline, like openpam_ttyconv() does. Clean up and simplify the environment handling code, which triggered an inexplicable bug on some systems. MFC after: 2 weeks
This commit is contained in:
parent
f9470900ef
commit
fc0cdb8504
@ -1,4 +1,5 @@
|
||||
.\" Copyright (c) 2001,2003 Networks Associates Technology, Inc.
|
||||
.\" Copyright (c) 2017 Dag-Erling Smørgrav
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Portions of this software were developed for the FreeBSD Project by
|
||||
@ -32,7 +33,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd February 8, 2012
|
||||
.Dd March 22, 2017
|
||||
.Dt PAM_EXEC 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -55,7 +56,19 @@ if the program name conflicts with an option name.
|
||||
.Pp
|
||||
The following options may be passed before the program and its
|
||||
arguments:
|
||||
.Bl -tag -width ".Cm return_prog_exit_status"
|
||||
.Bl -tag -width indent
|
||||
.It Cm capture_stderr
|
||||
Capture text printed by the program to its standard error stream and
|
||||
pass it to the conversation function as error messages.
|
||||
No attempt is made at buffering the text, so results may vary.
|
||||
.It Cm capture_stdout
|
||||
Capture text printed by the program to its standard output stream and
|
||||
pass it to the conversation function as informational messages.
|
||||
No attempt is made at buffering the text, so results may vary.
|
||||
.It Cm debug
|
||||
Ignored for compatibility reasons.
|
||||
.It Cm no_warn
|
||||
Ignored for compatibility reasons.
|
||||
.It Cm return_prog_exit_status
|
||||
Use the program exit status as the return code of the pam_sm_* function.
|
||||
It must be a valid return value for this function.
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*-
|
||||
* Copyright (c) 2001,2003 Networks Associates Technology, Inc.
|
||||
* Copyright (c) 2017 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed for the FreeBSD Project by ThinkSec AS and
|
||||
@ -36,9 +37,12 @@
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/procdesc.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -48,24 +52,62 @@ __FBSDID("$FreeBSD$");
|
||||
#include <security/pam_modules.h>
|
||||
#include <security/openpam.h>
|
||||
|
||||
#define ENV_ITEM(n) { (n), #n }
|
||||
#define PAM_ITEM_ENV(n) { (n), #n }
|
||||
static struct {
|
||||
int item;
|
||||
const char *name;
|
||||
} env_items[] = {
|
||||
ENV_ITEM(PAM_SERVICE),
|
||||
ENV_ITEM(PAM_USER),
|
||||
ENV_ITEM(PAM_TTY),
|
||||
ENV_ITEM(PAM_RHOST),
|
||||
ENV_ITEM(PAM_RUSER),
|
||||
} pam_item_env[] = {
|
||||
PAM_ITEM_ENV(PAM_SERVICE),
|
||||
PAM_ITEM_ENV(PAM_USER),
|
||||
PAM_ITEM_ENV(PAM_TTY),
|
||||
PAM_ITEM_ENV(PAM_RHOST),
|
||||
PAM_ITEM_ENV(PAM_RUSER),
|
||||
};
|
||||
#define NUM_PAM_ITEM_ENV (sizeof(pam_item_env) / sizeof(pam_item_env[0]))
|
||||
|
||||
#define PAM_ERR_ENV_X(str, num) str "=" #num
|
||||
#define PAM_ERR_ENV(pam_err) PAM_ERR_ENV_X(#pam_err, pam_err)
|
||||
static const char *pam_err_env[] = {
|
||||
PAM_ERR_ENV(PAM_SUCCESS),
|
||||
PAM_ERR_ENV(PAM_OPEN_ERR),
|
||||
PAM_ERR_ENV(PAM_SYMBOL_ERR),
|
||||
PAM_ERR_ENV(PAM_SERVICE_ERR),
|
||||
PAM_ERR_ENV(PAM_SYSTEM_ERR),
|
||||
PAM_ERR_ENV(PAM_BUF_ERR),
|
||||
PAM_ERR_ENV(PAM_CONV_ERR),
|
||||
PAM_ERR_ENV(PAM_PERM_DENIED),
|
||||
PAM_ERR_ENV(PAM_MAXTRIES),
|
||||
PAM_ERR_ENV(PAM_AUTH_ERR),
|
||||
PAM_ERR_ENV(PAM_NEW_AUTHTOK_REQD),
|
||||
PAM_ERR_ENV(PAM_CRED_INSUFFICIENT),
|
||||
PAM_ERR_ENV(PAM_AUTHINFO_UNAVAIL),
|
||||
PAM_ERR_ENV(PAM_USER_UNKNOWN),
|
||||
PAM_ERR_ENV(PAM_CRED_UNAVAIL),
|
||||
PAM_ERR_ENV(PAM_CRED_EXPIRED),
|
||||
PAM_ERR_ENV(PAM_CRED_ERR),
|
||||
PAM_ERR_ENV(PAM_ACCT_EXPIRED),
|
||||
PAM_ERR_ENV(PAM_AUTHTOK_EXPIRED),
|
||||
PAM_ERR_ENV(PAM_SESSION_ERR),
|
||||
PAM_ERR_ENV(PAM_AUTHTOK_ERR),
|
||||
PAM_ERR_ENV(PAM_AUTHTOK_RECOVERY_ERR),
|
||||
PAM_ERR_ENV(PAM_AUTHTOK_LOCK_BUSY),
|
||||
PAM_ERR_ENV(PAM_AUTHTOK_DISABLE_AGING),
|
||||
PAM_ERR_ENV(PAM_NO_MODULE_DATA),
|
||||
PAM_ERR_ENV(PAM_IGNORE),
|
||||
PAM_ERR_ENV(PAM_ABORT),
|
||||
PAM_ERR_ENV(PAM_TRY_AGAIN),
|
||||
PAM_ERR_ENV(PAM_MODULE_UNKNOWN),
|
||||
PAM_ERR_ENV(PAM_DOMAIN_UNKNOWN),
|
||||
PAM_ERR_ENV(PAM_NUM_ERR),
|
||||
};
|
||||
#define NUM_PAM_ERR_ENV (sizeof(pam_err_env) / sizeof(pam_err_env[0]))
|
||||
|
||||
struct pe_opts {
|
||||
int return_prog_exit_status;
|
||||
int capture_stdout;
|
||||
int capture_stderr;
|
||||
};
|
||||
|
||||
#define PAM_RV_COUNT 24
|
||||
|
||||
static int
|
||||
parse_options(const char *func, int *argc, const char **argv[],
|
||||
struct pe_opts *options)
|
||||
@ -79,22 +121,27 @@ parse_options(const char *func, int *argc, const char **argv[],
|
||||
* --:
|
||||
* stop options parsing; what follows is the command to execute
|
||||
*/
|
||||
options->return_prog_exit_status = 0;
|
||||
memset(options, 0, sizeof(*options));
|
||||
|
||||
for (i = 0; i < *argc; ++i) {
|
||||
if (strcmp((*argv)[i], "return_prog_exit_status") == 0) {
|
||||
openpam_log(PAM_LOG_DEBUG,
|
||||
"%s: Option \"return_prog_exit_status\" enabled",
|
||||
func);
|
||||
if (strcmp((*argv)[i], "debug") == 0 ||
|
||||
strcmp((*argv)[i], "no_warn") == 0) {
|
||||
/* ignore */
|
||||
} else if (strcmp((*argv)[i], "capture_stdout") == 0) {
|
||||
options->capture_stdout = 1;
|
||||
} else if (strcmp((*argv)[i], "capture_stderr") == 0) {
|
||||
options->capture_stderr = 1;
|
||||
} else if (strcmp((*argv)[i], "return_prog_exit_status") == 0) {
|
||||
options->return_prog_exit_status = 1;
|
||||
} else {
|
||||
if (strcmp((*argv)[i], "--") == 0) {
|
||||
(*argc)--;
|
||||
(*argv)++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
openpam_log(PAM_LOG_DEBUG, "%s: option \"%s\" enabled",
|
||||
func, (*argv)[i]);
|
||||
}
|
||||
|
||||
(*argc) -= i;
|
||||
@ -104,159 +151,229 @@ parse_options(const char *func, int *argc, const char **argv[],
|
||||
}
|
||||
|
||||
static int
|
||||
_pam_exec(pam_handle_t *pamh __unused,
|
||||
_pam_exec(pam_handle_t *pamh,
|
||||
const char *func, int flags __unused, int argc, const char *argv[],
|
||||
struct pe_opts *options)
|
||||
{
|
||||
int envlen, i, nitems, pam_err, status;
|
||||
int nitems_rv;
|
||||
char **envlist, **tmp, *envstr;
|
||||
volatile int childerr;
|
||||
char buf[PAM_MAX_MSG_SIZE];
|
||||
struct pollfd pfd[3];
|
||||
const void *item;
|
||||
char **envlist, *envstr, *resp, **tmp;
|
||||
ssize_t rlen;
|
||||
int envlen, extralen, i;
|
||||
int pam_err, serrno, status;
|
||||
int chout[2], cherr[2], pd;
|
||||
nfds_t nfds;
|
||||
pid_t pid;
|
||||
|
||||
/*
|
||||
* XXX For additional credit, divert child's stdin/stdout/stderr
|
||||
* to the conversation function.
|
||||
*/
|
||||
pd = -1;
|
||||
pid = 0;
|
||||
chout[0] = chout[1] = cherr[0] = cherr[1] = -1;
|
||||
envlist = NULL;
|
||||
|
||||
#define OUT(ret) do { pam_err = (ret); goto out; } while (0)
|
||||
|
||||
/* Check there's a program name left after parsing options. */
|
||||
if (argc < 1) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: No program specified: aborting",
|
||||
func);
|
||||
return (PAM_SERVICE_ERR);
|
||||
OUT(PAM_SERVICE_ERR);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the child's environment list. It consists of the PAM
|
||||
* environment, plus a few hand-picked PAM items, the pam_sm_*
|
||||
* function name calling it and, if return_prog_exit_status is
|
||||
* set, the valid return codes numerical values.
|
||||
* Set up the child's environment list. It consists of the PAM
|
||||
* environment, a few hand-picked PAM items, the name of the
|
||||
* service function, and if return_prog_exit_status is set, the
|
||||
* numerical values of all PAM error codes.
|
||||
*/
|
||||
|
||||
/* compute the final size of the environment. */
|
||||
envlist = pam_getenvlist(pamh);
|
||||
for (envlen = 0; envlist[envlen] != NULL; ++envlen)
|
||||
/* nothing */ ;
|
||||
nitems = sizeof(env_items) / sizeof(*env_items);
|
||||
/* Count PAM return values put in the environment. */
|
||||
nitems_rv = options->return_prog_exit_status ? PAM_RV_COUNT : 0;
|
||||
tmp = realloc(envlist, (envlen + nitems + 1 + nitems_rv + 1) *
|
||||
sizeof(*envlist));
|
||||
if (tmp == NULL) {
|
||||
openpam_free_envlist(envlist);
|
||||
return (PAM_BUF_ERR);
|
||||
}
|
||||
extralen = NUM_PAM_ITEM_ENV + 1;
|
||||
if (options->return_prog_exit_status)
|
||||
extralen += NUM_PAM_ERR_ENV;
|
||||
tmp = reallocarray(envlist, envlen + extralen + 1, sizeof(*envlist));
|
||||
openpam_log(PAM_LOG_DEBUG, "envlen = %d extralen = %d tmp = %p",
|
||||
envlen, extralen, tmp);
|
||||
if (tmp == NULL)
|
||||
OUT(PAM_BUF_ERR);
|
||||
envlist = tmp;
|
||||
for (i = 0; i < nitems; ++i) {
|
||||
const void *item;
|
||||
extralen += envlen;
|
||||
|
||||
pam_err = pam_get_item(pamh, env_items[i].item, &item);
|
||||
/* copy selected PAM items to the environment */
|
||||
for (i = 0; i < NUM_PAM_ITEM_ENV; ++i) {
|
||||
pam_err = pam_get_item(pamh, pam_item_env[i].item, &item);
|
||||
if (pam_err != PAM_SUCCESS || item == NULL)
|
||||
continue;
|
||||
asprintf(&envstr, "%s=%s", env_items[i].name,
|
||||
(const char *)item);
|
||||
if (envstr == NULL) {
|
||||
openpam_free_envlist(envlist);
|
||||
return (PAM_BUF_ERR);
|
||||
}
|
||||
if (asprintf(&envstr, "%s=%s", pam_item_env[i].name, item) < 0)
|
||||
OUT(PAM_BUF_ERR);
|
||||
envlist[envlen++] = envstr;
|
||||
envlist[envlen] = NULL;
|
||||
openpam_log(PAM_LOG_DEBUG, "setenv %s", envstr);
|
||||
}
|
||||
|
||||
/* Add the pam_sm_* function name to the environment. */
|
||||
asprintf(&envstr, "PAM_SM_FUNC=%s", func);
|
||||
if (envstr == NULL) {
|
||||
openpam_free_envlist(envlist);
|
||||
return (PAM_BUF_ERR);
|
||||
}
|
||||
/* add the name of the service function to the environment */
|
||||
if (asprintf(&envstr, "PAM_SM_FUNC=%s", func) < 0)
|
||||
OUT(PAM_BUF_ERR);
|
||||
envlist[envlen++] = envstr;
|
||||
|
||||
/* Add the PAM return values to the environment. */
|
||||
if (options->return_prog_exit_status) {
|
||||
#define ADD_PAM_RV_TO_ENV(name) \
|
||||
asprintf(&envstr, #name "=%d", name); \
|
||||
if (envstr == NULL) { \
|
||||
openpam_free_envlist(envlist); \
|
||||
return (PAM_BUF_ERR); \
|
||||
} \
|
||||
envlist[envlen++] = envstr
|
||||
/*
|
||||
* CAUTION: When adding/removing an item in the list
|
||||
* below, be sure to update the value of PAM_RV_COUNT.
|
||||
*/
|
||||
ADD_PAM_RV_TO_ENV(PAM_ABORT);
|
||||
ADD_PAM_RV_TO_ENV(PAM_ACCT_EXPIRED);
|
||||
ADD_PAM_RV_TO_ENV(PAM_AUTHINFO_UNAVAIL);
|
||||
ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_DISABLE_AGING);
|
||||
ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_ERR);
|
||||
ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_LOCK_BUSY);
|
||||
ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_RECOVERY_ERR);
|
||||
ADD_PAM_RV_TO_ENV(PAM_AUTH_ERR);
|
||||
ADD_PAM_RV_TO_ENV(PAM_BUF_ERR);
|
||||
ADD_PAM_RV_TO_ENV(PAM_CONV_ERR);
|
||||
ADD_PAM_RV_TO_ENV(PAM_CRED_ERR);
|
||||
ADD_PAM_RV_TO_ENV(PAM_CRED_EXPIRED);
|
||||
ADD_PAM_RV_TO_ENV(PAM_CRED_INSUFFICIENT);
|
||||
ADD_PAM_RV_TO_ENV(PAM_CRED_UNAVAIL);
|
||||
ADD_PAM_RV_TO_ENV(PAM_IGNORE);
|
||||
ADD_PAM_RV_TO_ENV(PAM_MAXTRIES);
|
||||
ADD_PAM_RV_TO_ENV(PAM_NEW_AUTHTOK_REQD);
|
||||
ADD_PAM_RV_TO_ENV(PAM_PERM_DENIED);
|
||||
ADD_PAM_RV_TO_ENV(PAM_SERVICE_ERR);
|
||||
ADD_PAM_RV_TO_ENV(PAM_SESSION_ERR);
|
||||
ADD_PAM_RV_TO_ENV(PAM_SUCCESS);
|
||||
ADD_PAM_RV_TO_ENV(PAM_SYSTEM_ERR);
|
||||
ADD_PAM_RV_TO_ENV(PAM_TRY_AGAIN);
|
||||
ADD_PAM_RV_TO_ENV(PAM_USER_UNKNOWN);
|
||||
}
|
||||
|
||||
envlist[envlen] = NULL;
|
||||
|
||||
/*
|
||||
* Fork and run the command. By using vfork() instead of fork(),
|
||||
* we can distinguish between an execve() failure and a non-zero
|
||||
* exit status from the command.
|
||||
*/
|
||||
childerr = 0;
|
||||
if ((pid = vfork()) == 0) {
|
||||
execve(argv[0], (char * const *)argv, (char * const *)envlist);
|
||||
childerr = errno;
|
||||
/* add the PAM error codes to the environment. */
|
||||
if (options->return_prog_exit_status) {
|
||||
for (i = 0; i < (int)NUM_PAM_ERR_ENV; ++i) {
|
||||
if ((envstr = strdup(pam_err_env[i])) == NULL)
|
||||
OUT(PAM_BUF_ERR);
|
||||
envlist[envlen++] = envstr;
|
||||
envlist[envlen] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
openpam_log(PAM_LOG_DEBUG, "envlen = %d extralen = %d envlist = %p",
|
||||
envlen, extralen, envlist);
|
||||
|
||||
/* set up pipes if capture was requested */
|
||||
if (options->capture_stdout) {
|
||||
if (pipe(chout) != 0) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: pipe(): %m", func);
|
||||
OUT(PAM_SYSTEM_ERR);
|
||||
}
|
||||
if (fcntl(chout[0], F_SETFL, O_NONBLOCK) != 0) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: fcntl(): %m", func);
|
||||
OUT(PAM_SYSTEM_ERR);
|
||||
}
|
||||
} else {
|
||||
if ((chout[1] = open("/dev/null", O_RDWR)) < 0) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: /dev/null: %m", func);
|
||||
OUT(PAM_SYSTEM_ERR);
|
||||
}
|
||||
}
|
||||
if (options->capture_stderr) {
|
||||
if (pipe(cherr) != 0) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: pipe(): %m", func);
|
||||
OUT(PAM_SYSTEM_ERR);
|
||||
}
|
||||
if (fcntl(cherr[0], F_SETFL, O_NONBLOCK) != 0) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: fcntl(): %m", func);
|
||||
OUT(PAM_SYSTEM_ERR);
|
||||
}
|
||||
} else {
|
||||
if ((cherr[1] = open("/dev/null", O_RDWR)) < 0) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: /dev/null: %m", func);
|
||||
OUT(PAM_SYSTEM_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
if ((pid = pdfork(&pd, 0)) == 0) {
|
||||
/* child */
|
||||
if ((chout[0] >= 0 && close(chout[0]) != 0) ||
|
||||
(cherr[0] >= 0 && close(cherr[0]) != 0)) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: close(): %m", func);
|
||||
} else if (dup2(chout[1], STDOUT_FILENO) != STDOUT_FILENO ||
|
||||
dup2(cherr[1], STDERR_FILENO) != STDERR_FILENO) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: dup2(): %m", func);
|
||||
} else {
|
||||
execve(argv[0], (char * const *)argv,
|
||||
(char * const *)envlist);
|
||||
openpam_log(PAM_LOG_ERROR, "%s: execve(%s): %m",
|
||||
func, argv[0]);
|
||||
}
|
||||
_exit(1);
|
||||
}
|
||||
openpam_free_envlist(envlist);
|
||||
/* parent */
|
||||
if (pid == -1) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: vfork(): %m", func);
|
||||
return (PAM_SYSTEM_ERR);
|
||||
openpam_log(PAM_LOG_ERROR, "%s: pdfork(): %m", func);
|
||||
OUT(PAM_SYSTEM_ERR);
|
||||
}
|
||||
/* use poll() to watch the process and stdout / stderr */
|
||||
if (chout[1] >= 0)
|
||||
close(chout[1]);
|
||||
if (cherr[1] >= 0)
|
||||
close(cherr[1]);
|
||||
memset(pfd, 0, sizeof pfd);
|
||||
pfd[0].fd = pd;
|
||||
pfd[0].events = POLLHUP;
|
||||
nfds = 1;
|
||||
if (options->capture_stdout) {
|
||||
pfd[nfds].fd = chout[0];
|
||||
pfd[nfds].events = POLLIN|POLLERR|POLLHUP;
|
||||
nfds++;
|
||||
}
|
||||
if (options->capture_stderr) {
|
||||
pfd[nfds].fd = cherr[0];
|
||||
pfd[nfds].events = POLLIN|POLLERR|POLLHUP;
|
||||
nfds++;
|
||||
}
|
||||
|
||||
/* loop until the process exits */
|
||||
do {
|
||||
if (poll(pfd, nfds, INFTIM) < 0) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: poll(): %m", func);
|
||||
OUT(PAM_SYSTEM_ERR);
|
||||
}
|
||||
for (i = 1; i < nfds; ++i) {
|
||||
if ((pfd[i].revents & POLLIN) == 0)
|
||||
continue;
|
||||
if ((rlen = read(pfd[i].fd, buf, sizeof(buf) - 1)) < 0) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: read(): %m",
|
||||
func);
|
||||
OUT(PAM_SYSTEM_ERR);
|
||||
} else if (rlen == 0) {
|
||||
continue;
|
||||
}
|
||||
buf[rlen] = '\0';
|
||||
(void)pam_prompt(pamh, pfd[i].fd == chout[0] ?
|
||||
PAM_TEXT_INFO : PAM_ERROR_MSG, &resp, "%s", buf);
|
||||
}
|
||||
} while (pfd[0].revents == 0);
|
||||
|
||||
/* the child process has exited */
|
||||
while (waitpid(pid, &status, 0) == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
openpam_log(PAM_LOG_ERROR, "%s: waitpid(): %m", func);
|
||||
return (PAM_SYSTEM_ERR);
|
||||
}
|
||||
if (childerr != 0) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: execve(): %m", func);
|
||||
return (PAM_SYSTEM_ERR);
|
||||
OUT(PAM_SYSTEM_ERR);
|
||||
}
|
||||
|
||||
/* check exit code */
|
||||
if (WIFSIGNALED(status)) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: %s caught signal %d%s",
|
||||
func, argv[0], WTERMSIG(status),
|
||||
WCOREDUMP(status) ? " (core dumped)" : "");
|
||||
return (PAM_SERVICE_ERR);
|
||||
OUT(PAM_SERVICE_ERR);
|
||||
}
|
||||
if (!WIFEXITED(status)) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: unknown status 0x%x",
|
||||
func, status);
|
||||
return (PAM_SERVICE_ERR);
|
||||
OUT(PAM_SERVICE_ERR);
|
||||
}
|
||||
|
||||
if (options->return_prog_exit_status) {
|
||||
openpam_log(PAM_LOG_DEBUG,
|
||||
"%s: Use program exit status as return value: %d",
|
||||
func, WEXITSTATUS(status));
|
||||
return (WEXITSTATUS(status));
|
||||
OUT(WEXITSTATUS(status));
|
||||
} else {
|
||||
return (WEXITSTATUS(status) == 0 ?
|
||||
PAM_SUCCESS : PAM_PERM_DENIED);
|
||||
OUT(WEXITSTATUS(status) == 0 ? PAM_SUCCESS : PAM_PERM_DENIED);
|
||||
}
|
||||
/* unreachable */
|
||||
out:
|
||||
serrno = errno;
|
||||
if (pd >= 0)
|
||||
close(pd);
|
||||
if (chout[0] >= 0)
|
||||
close(chout[0]);
|
||||
if (chout[1] >= 0)
|
||||
close(chout[1]);
|
||||
if (cherr[0] >= 0)
|
||||
close(cherr[0]);
|
||||
if (cherr[0] >= 0)
|
||||
close(cherr[1]);
|
||||
if (envlist != NULL)
|
||||
openpam_free_envlist(envlist);
|
||||
errno = serrno;
|
||||
return (pam_err);
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
|
Loading…
x
Reference in New Issue
Block a user