xref: /freebsd/lib/libpam/modules/pam_exec/pam_exec.c (revision 5e53a4f90f82c4345f277dd87cc9292f26e04a29)
1f65b2180SDag-Erling Smørgrav /*-
2*5e53a4f9SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
3*5e53a4f9SPedro F. Giffuni  *
437def36fSDag-Erling Smørgrav  * Copyright (c) 2001,2003 Networks Associates Technology, Inc.
53869fb78SDag-Erling Smørgrav  * Copyright (c) 2017 Dag-Erling Smørgrav
6f65b2180SDag-Erling Smørgrav  * All rights reserved.
7f65b2180SDag-Erling Smørgrav  *
8f65b2180SDag-Erling Smørgrav  * This software was developed for the FreeBSD Project by ThinkSec AS and
9f65b2180SDag-Erling Smørgrav  * NAI Labs, the Security Research Division of Network Associates, Inc.
10f65b2180SDag-Erling Smørgrav  * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
11f65b2180SDag-Erling Smørgrav  * DARPA CHATS research program.
12f65b2180SDag-Erling Smørgrav  *
13f65b2180SDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
14f65b2180SDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
15f65b2180SDag-Erling Smørgrav  * are met:
16f65b2180SDag-Erling Smørgrav  * 1. Redistributions of source code must retain the above copyright
17f65b2180SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer.
18f65b2180SDag-Erling Smørgrav  * 2. Redistributions in binary form must reproduce the above copyright
19f65b2180SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer in the
20f65b2180SDag-Erling Smørgrav  *    documentation and/or other materials provided with the distribution.
21f65b2180SDag-Erling Smørgrav  * 3. The name of the author may not be used to endorse or promote
22f65b2180SDag-Erling Smørgrav  *    products derived from this software without specific prior written
23f65b2180SDag-Erling Smørgrav  *    permission.
24f65b2180SDag-Erling Smørgrav  *
25f65b2180SDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
26f65b2180SDag-Erling Smørgrav  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27f65b2180SDag-Erling Smørgrav  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28f65b2180SDag-Erling Smørgrav  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
29f65b2180SDag-Erling Smørgrav  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30f65b2180SDag-Erling Smørgrav  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31f65b2180SDag-Erling Smørgrav  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32f65b2180SDag-Erling Smørgrav  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33f65b2180SDag-Erling Smørgrav  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34f65b2180SDag-Erling Smørgrav  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35f65b2180SDag-Erling Smørgrav  * SUCH DAMAGE.
36f65b2180SDag-Erling Smørgrav  */
37f65b2180SDag-Erling Smørgrav 
38f65b2180SDag-Erling Smørgrav #include <sys/cdefs.h>
39f65b2180SDag-Erling Smørgrav __FBSDID("$FreeBSD$");
40f65b2180SDag-Erling Smørgrav 
41f65b2180SDag-Erling Smørgrav #include <sys/types.h>
423869fb78SDag-Erling Smørgrav #include <sys/poll.h>
433869fb78SDag-Erling Smørgrav #include <sys/procdesc.h>
44f65b2180SDag-Erling Smørgrav #include <sys/wait.h>
45f65b2180SDag-Erling Smørgrav 
46f65b2180SDag-Erling Smørgrav #include <errno.h>
473869fb78SDag-Erling Smørgrav #include <fcntl.h>
488bdb099aSEd Schouten #include <stdio.h>
49f65b2180SDag-Erling Smørgrav #include <stdlib.h>
50f65b2180SDag-Erling Smørgrav #include <string.h>
51f65b2180SDag-Erling Smørgrav #include <unistd.h>
52f65b2180SDag-Erling Smørgrav 
53f65b2180SDag-Erling Smørgrav #include <security/pam_appl.h>
54f65b2180SDag-Erling Smørgrav #include <security/pam_modules.h>
55f65b2180SDag-Erling Smørgrav #include <security/openpam.h>
56f65b2180SDag-Erling Smørgrav 
573869fb78SDag-Erling Smørgrav #define PAM_ITEM_ENV(n) { (n), #n }
589d97c7eeSDag-Erling Smørgrav static struct {
599d97c7eeSDag-Erling Smørgrav 	int item;
609d97c7eeSDag-Erling Smørgrav 	const char *name;
613869fb78SDag-Erling Smørgrav } pam_item_env[] = {
623869fb78SDag-Erling Smørgrav 	PAM_ITEM_ENV(PAM_SERVICE),
633869fb78SDag-Erling Smørgrav 	PAM_ITEM_ENV(PAM_USER),
643869fb78SDag-Erling Smørgrav 	PAM_ITEM_ENV(PAM_TTY),
653869fb78SDag-Erling Smørgrav 	PAM_ITEM_ENV(PAM_RHOST),
663869fb78SDag-Erling Smørgrav 	PAM_ITEM_ENV(PAM_RUSER),
679d97c7eeSDag-Erling Smørgrav };
683869fb78SDag-Erling Smørgrav #define NUM_PAM_ITEM_ENV (sizeof(pam_item_env) / sizeof(pam_item_env[0]))
693869fb78SDag-Erling Smørgrav 
703869fb78SDag-Erling Smørgrav #define PAM_ERR_ENV_X(str, num) str "=" #num
713869fb78SDag-Erling Smørgrav #define PAM_ERR_ENV(pam_err) PAM_ERR_ENV_X(#pam_err, pam_err)
723869fb78SDag-Erling Smørgrav static const char *pam_err_env[] = {
733869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_SUCCESS),
743869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_OPEN_ERR),
753869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_SYMBOL_ERR),
763869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_SERVICE_ERR),
773869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_SYSTEM_ERR),
783869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_BUF_ERR),
793869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_CONV_ERR),
803869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_PERM_DENIED),
813869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_MAXTRIES),
823869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_AUTH_ERR),
833869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_NEW_AUTHTOK_REQD),
843869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_CRED_INSUFFICIENT),
853869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_AUTHINFO_UNAVAIL),
863869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_USER_UNKNOWN),
873869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_CRED_UNAVAIL),
883869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_CRED_EXPIRED),
893869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_CRED_ERR),
903869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_ACCT_EXPIRED),
913869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_AUTHTOK_EXPIRED),
923869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_SESSION_ERR),
933869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_AUTHTOK_ERR),
943869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_AUTHTOK_RECOVERY_ERR),
953869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_AUTHTOK_LOCK_BUSY),
963869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_AUTHTOK_DISABLE_AGING),
973869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_NO_MODULE_DATA),
983869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_IGNORE),
993869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_ABORT),
1003869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_TRY_AGAIN),
1013869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_MODULE_UNKNOWN),
1023869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_DOMAIN_UNKNOWN),
1033869fb78SDag-Erling Smørgrav 	PAM_ERR_ENV(PAM_NUM_ERR),
1043869fb78SDag-Erling Smørgrav };
1053869fb78SDag-Erling Smørgrav #define NUM_PAM_ERR_ENV (sizeof(pam_err_env) / sizeof(pam_err_env[0]))
1069d97c7eeSDag-Erling Smørgrav 
1073902d8a9SJean-Sébastien Pédron struct pe_opts {
1083902d8a9SJean-Sébastien Pédron 	int	return_prog_exit_status;
1093869fb78SDag-Erling Smørgrav 	int	capture_stdout;
1103869fb78SDag-Erling Smørgrav 	int	capture_stderr;
1113902d8a9SJean-Sébastien Pédron };
1123902d8a9SJean-Sébastien Pédron 
113f65b2180SDag-Erling Smørgrav static int
1143902d8a9SJean-Sébastien Pédron parse_options(const char *func, int *argc, const char **argv[],
1153902d8a9SJean-Sébastien Pédron     struct pe_opts *options)
116f65b2180SDag-Erling Smørgrav {
1173902d8a9SJean-Sébastien Pédron 	int i;
1189d97c7eeSDag-Erling Smørgrav 
1199d97c7eeSDag-Erling Smørgrav 	/*
1207e3d5c1fSJean-Sébastien Pédron 	 * Parse options:
1217e3d5c1fSJean-Sébastien Pédron 	 *   return_prog_exit_status:
1227e3d5c1fSJean-Sébastien Pédron 	 *     use the program exit status as the return code of pam_exec
1237e3d5c1fSJean-Sébastien Pédron 	 *   --:
1247e3d5c1fSJean-Sébastien Pédron 	 *     stop options parsing; what follows is the command to execute
1257e3d5c1fSJean-Sébastien Pédron 	 */
1263869fb78SDag-Erling Smørgrav 	memset(options, 0, sizeof(*options));
1273902d8a9SJean-Sébastien Pédron 
1283902d8a9SJean-Sébastien Pédron 	for (i = 0; i < *argc; ++i) {
1293869fb78SDag-Erling Smørgrav 		if (strcmp((*argv)[i], "debug") == 0 ||
1303869fb78SDag-Erling Smørgrav 		    strcmp((*argv)[i], "no_warn") == 0) {
1313869fb78SDag-Erling Smørgrav 			/* ignore */
1323869fb78SDag-Erling Smørgrav 		} else if (strcmp((*argv)[i], "capture_stdout") == 0) {
1333869fb78SDag-Erling Smørgrav 			options->capture_stdout = 1;
1343869fb78SDag-Erling Smørgrav 		} else if (strcmp((*argv)[i], "capture_stderr") == 0) {
1353869fb78SDag-Erling Smørgrav 			options->capture_stderr = 1;
1363869fb78SDag-Erling Smørgrav 		} else if (strcmp((*argv)[i], "return_prog_exit_status") == 0) {
1373902d8a9SJean-Sébastien Pédron 			options->return_prog_exit_status = 1;
1387e3d5c1fSJean-Sébastien Pédron 		} else {
1393902d8a9SJean-Sébastien Pédron 			if (strcmp((*argv)[i], "--") == 0) {
1403902d8a9SJean-Sébastien Pédron 				(*argc)--;
1413902d8a9SJean-Sébastien Pédron 				(*argv)++;
1427e3d5c1fSJean-Sébastien Pédron 			}
1437e3d5c1fSJean-Sébastien Pédron 			break;
1447e3d5c1fSJean-Sébastien Pédron 		}
1453869fb78SDag-Erling Smørgrav 		openpam_log(PAM_LOG_DEBUG, "%s: option \"%s\" enabled",
1463869fb78SDag-Erling Smørgrav 		    func, (*argv)[i]);
1477e3d5c1fSJean-Sébastien Pédron 	}
1487e3d5c1fSJean-Sébastien Pédron 
1493902d8a9SJean-Sébastien Pédron 	(*argc) -= i;
1503902d8a9SJean-Sébastien Pédron 	(*argv) += i;
1513902d8a9SJean-Sébastien Pédron 
1523902d8a9SJean-Sébastien Pédron 	return (0);
1533902d8a9SJean-Sébastien Pédron }
1543902d8a9SJean-Sébastien Pédron 
1553902d8a9SJean-Sébastien Pédron static int
1563869fb78SDag-Erling Smørgrav _pam_exec(pam_handle_t *pamh,
1573902d8a9SJean-Sébastien Pédron     const char *func, int flags __unused, int argc, const char *argv[],
1583902d8a9SJean-Sébastien Pédron     struct pe_opts *options)
1593902d8a9SJean-Sébastien Pédron {
1603869fb78SDag-Erling Smørgrav 	char buf[PAM_MAX_MSG_SIZE];
1613869fb78SDag-Erling Smørgrav 	struct pollfd pfd[3];
1623869fb78SDag-Erling Smørgrav 	const void *item;
1633869fb78SDag-Erling Smørgrav 	char **envlist, *envstr, *resp, **tmp;
1643869fb78SDag-Erling Smørgrav 	ssize_t rlen;
1653869fb78SDag-Erling Smørgrav 	int envlen, extralen, i;
1663869fb78SDag-Erling Smørgrav 	int pam_err, serrno, status;
1673869fb78SDag-Erling Smørgrav 	int chout[2], cherr[2], pd;
1683869fb78SDag-Erling Smørgrav 	nfds_t nfds;
1693902d8a9SJean-Sébastien Pédron 	pid_t pid;
1703902d8a9SJean-Sébastien Pédron 
1713869fb78SDag-Erling Smørgrav 	pd = -1;
1723869fb78SDag-Erling Smørgrav 	pid = 0;
1733869fb78SDag-Erling Smørgrav 	chout[0] = chout[1] = cherr[0] = cherr[1] = -1;
1743869fb78SDag-Erling Smørgrav 	envlist = NULL;
1753869fb78SDag-Erling Smørgrav 
1763869fb78SDag-Erling Smørgrav #define OUT(ret) do { pam_err = (ret); goto out; } while (0)
1777e3d5c1fSJean-Sébastien Pédron 
1787e3d5c1fSJean-Sébastien Pédron 	/* Check there's a program name left after parsing options. */
1797e3d5c1fSJean-Sébastien Pédron 	if (argc < 1) {
1807e3d5c1fSJean-Sébastien Pédron 		openpam_log(PAM_LOG_ERROR, "%s: No program specified: aborting",
1817e3d5c1fSJean-Sébastien Pédron 		    func);
1823869fb78SDag-Erling Smørgrav 		OUT(PAM_SERVICE_ERR);
1837e3d5c1fSJean-Sébastien Pédron 	}
1847e3d5c1fSJean-Sébastien Pédron 
1857e3d5c1fSJean-Sébastien Pédron 	/*
1869d97c7eeSDag-Erling Smørgrav 	 * Set up the child's environment list.  It consists of the PAM
1873869fb78SDag-Erling Smørgrav 	 * environment, a few hand-picked PAM items, the name of the
1883869fb78SDag-Erling Smørgrav 	 * service function, and if return_prog_exit_status is set, the
1893869fb78SDag-Erling Smørgrav 	 * numerical values of all PAM error codes.
1909d97c7eeSDag-Erling Smørgrav 	 */
1913869fb78SDag-Erling Smørgrav 
1923869fb78SDag-Erling Smørgrav 	/* compute the final size of the environment. */
193a76a4d44SDag-Erling Smørgrav 	envlist = pam_getenvlist(pamh);
1949d97c7eeSDag-Erling Smørgrav 	for (envlen = 0; envlist[envlen] != NULL; ++envlen)
1959d97c7eeSDag-Erling Smørgrav 		/* nothing */ ;
1963869fb78SDag-Erling Smørgrav 	extralen = NUM_PAM_ITEM_ENV + 1;
1973869fb78SDag-Erling Smørgrav 	if (options->return_prog_exit_status)
1983869fb78SDag-Erling Smørgrav 		extralen += NUM_PAM_ERR_ENV;
1993869fb78SDag-Erling Smørgrav 	tmp = reallocarray(envlist, envlen + extralen + 1, sizeof(*envlist));
2003869fb78SDag-Erling Smørgrav 	openpam_log(PAM_LOG_DEBUG, "envlen = %d extralen = %d tmp = %p",
2013869fb78SDag-Erling Smørgrav 	    envlen, extralen, tmp);
2023869fb78SDag-Erling Smørgrav 	if (tmp == NULL)
2033869fb78SDag-Erling Smørgrav 		OUT(PAM_BUF_ERR);
2049d97c7eeSDag-Erling Smørgrav 	envlist = tmp;
2053869fb78SDag-Erling Smørgrav 	extralen += envlen;
2069d97c7eeSDag-Erling Smørgrav 
2073869fb78SDag-Erling Smørgrav 	/* copy selected PAM items to the environment */
2083869fb78SDag-Erling Smørgrav 	for (i = 0; i < NUM_PAM_ITEM_ENV; ++i) {
2093869fb78SDag-Erling Smørgrav 		pam_err = pam_get_item(pamh, pam_item_env[i].item, &item);
2109d97c7eeSDag-Erling Smørgrav 		if (pam_err != PAM_SUCCESS || item == NULL)
2119d97c7eeSDag-Erling Smørgrav 			continue;
2123869fb78SDag-Erling Smørgrav 		if (asprintf(&envstr, "%s=%s", pam_item_env[i].name, item) < 0)
2133869fb78SDag-Erling Smørgrav 			OUT(PAM_BUF_ERR);
2149d97c7eeSDag-Erling Smørgrav 		envlist[envlen++] = envstr;
2159d97c7eeSDag-Erling Smørgrav 		envlist[envlen] = NULL;
2163869fb78SDag-Erling Smørgrav 		openpam_log(PAM_LOG_DEBUG, "setenv %s", envstr);
2179d97c7eeSDag-Erling Smørgrav 	}
2189d97c7eeSDag-Erling Smørgrav 
2193869fb78SDag-Erling Smørgrav 	/* add the name of the service function to the environment */
2203869fb78SDag-Erling Smørgrav 	if (asprintf(&envstr, "PAM_SM_FUNC=%s", func) < 0)
2213869fb78SDag-Erling Smørgrav 		OUT(PAM_BUF_ERR);
2227e3d5c1fSJean-Sébastien Pédron 	envlist[envlen++] = envstr;
2233869fb78SDag-Erling Smørgrav 	envlist[envlen] = NULL;
2247e3d5c1fSJean-Sébastien Pédron 
2253869fb78SDag-Erling Smørgrav 	/* add the PAM error codes to the environment. */
2263902d8a9SJean-Sébastien Pédron 	if (options->return_prog_exit_status) {
2273869fb78SDag-Erling Smørgrav 		for (i = 0; i < (int)NUM_PAM_ERR_ENV; ++i) {
2283869fb78SDag-Erling Smørgrav 			if ((envstr = strdup(pam_err_env[i])) == NULL)
2293869fb78SDag-Erling Smørgrav 				OUT(PAM_BUF_ERR);
2303869fb78SDag-Erling Smørgrav 			envlist[envlen++] = envstr;
2313869fb78SDag-Erling Smørgrav 			envlist[envlen] = NULL;
2323869fb78SDag-Erling Smørgrav 		}
2337e3d5c1fSJean-Sébastien Pédron 	}
2347e3d5c1fSJean-Sébastien Pédron 
2353869fb78SDag-Erling Smørgrav 	openpam_log(PAM_LOG_DEBUG, "envlen = %d extralen = %d envlist = %p",
2363869fb78SDag-Erling Smørgrav 	    envlen, extralen, envlist);
2377e3d5c1fSJean-Sébastien Pédron 
2383869fb78SDag-Erling Smørgrav 	/* set up pipes if capture was requested */
2393869fb78SDag-Erling Smørgrav 	if (options->capture_stdout) {
2403869fb78SDag-Erling Smørgrav 		if (pipe(chout) != 0) {
2413869fb78SDag-Erling Smørgrav 			openpam_log(PAM_LOG_ERROR, "%s: pipe(): %m", func);
2423869fb78SDag-Erling Smørgrav 			OUT(PAM_SYSTEM_ERR);
2433869fb78SDag-Erling Smørgrav 		}
2443869fb78SDag-Erling Smørgrav 		if (fcntl(chout[0], F_SETFL, O_NONBLOCK) != 0) {
2453869fb78SDag-Erling Smørgrav 			openpam_log(PAM_LOG_ERROR, "%s: fcntl(): %m", func);
2463869fb78SDag-Erling Smørgrav 			OUT(PAM_SYSTEM_ERR);
2473869fb78SDag-Erling Smørgrav 		}
2483869fb78SDag-Erling Smørgrav 	} else {
2493869fb78SDag-Erling Smørgrav 		if ((chout[1] = open("/dev/null", O_RDWR)) < 0) {
2503869fb78SDag-Erling Smørgrav 			openpam_log(PAM_LOG_ERROR, "%s: /dev/null: %m", func);
2513869fb78SDag-Erling Smørgrav 			OUT(PAM_SYSTEM_ERR);
2523869fb78SDag-Erling Smørgrav 		}
2533869fb78SDag-Erling Smørgrav 	}
2543869fb78SDag-Erling Smørgrav 	if (options->capture_stderr) {
2553869fb78SDag-Erling Smørgrav 		if (pipe(cherr) != 0) {
2563869fb78SDag-Erling Smørgrav 			openpam_log(PAM_LOG_ERROR, "%s: pipe(): %m", func);
2573869fb78SDag-Erling Smørgrav 			OUT(PAM_SYSTEM_ERR);
2583869fb78SDag-Erling Smørgrav 		}
2593869fb78SDag-Erling Smørgrav 		if (fcntl(cherr[0], F_SETFL, O_NONBLOCK) != 0) {
2603869fb78SDag-Erling Smørgrav 			openpam_log(PAM_LOG_ERROR, "%s: fcntl(): %m", func);
2613869fb78SDag-Erling Smørgrav 			OUT(PAM_SYSTEM_ERR);
2623869fb78SDag-Erling Smørgrav 		}
2633869fb78SDag-Erling Smørgrav 	} else {
2643869fb78SDag-Erling Smørgrav 		if ((cherr[1] = open("/dev/null", O_RDWR)) < 0) {
2653869fb78SDag-Erling Smørgrav 			openpam_log(PAM_LOG_ERROR, "%s: /dev/null: %m", func);
2663869fb78SDag-Erling Smørgrav 			OUT(PAM_SYSTEM_ERR);
2673869fb78SDag-Erling Smørgrav 		}
2683869fb78SDag-Erling Smørgrav 	}
2693869fb78SDag-Erling Smørgrav 
2703869fb78SDag-Erling Smørgrav 	if ((pid = pdfork(&pd, 0)) == 0) {
2713869fb78SDag-Erling Smørgrav 		/* child */
2723869fb78SDag-Erling Smørgrav 		if ((chout[0] >= 0 && close(chout[0]) != 0) ||
2733869fb78SDag-Erling Smørgrav 		    (cherr[0] >= 0 && close(cherr[0]) != 0)) {
2743869fb78SDag-Erling Smørgrav 			openpam_log(PAM_LOG_ERROR, "%s: close(): %m", func);
2753869fb78SDag-Erling Smørgrav 		} else if (dup2(chout[1], STDOUT_FILENO) != STDOUT_FILENO ||
2763869fb78SDag-Erling Smørgrav 		    dup2(cherr[1], STDERR_FILENO) != STDERR_FILENO) {
2773869fb78SDag-Erling Smørgrav 			openpam_log(PAM_LOG_ERROR, "%s: dup2(): %m", func);
2783869fb78SDag-Erling Smørgrav 		} else {
2793869fb78SDag-Erling Smørgrav 			execve(argv[0], (char * const *)argv,
2803869fb78SDag-Erling Smørgrav 			    (char * const *)envlist);
2813869fb78SDag-Erling Smørgrav 			openpam_log(PAM_LOG_ERROR, "%s: execve(%s): %m",
2823869fb78SDag-Erling Smørgrav 			    func, argv[0]);
2833869fb78SDag-Erling Smørgrav 		}
284f65b2180SDag-Erling Smørgrav 		_exit(1);
285a76a4d44SDag-Erling Smørgrav 	}
2863869fb78SDag-Erling Smørgrav 	/* parent */
287a76a4d44SDag-Erling Smørgrav 	if (pid == -1) {
2883869fb78SDag-Erling Smørgrav 		openpam_log(PAM_LOG_ERROR, "%s: pdfork(): %m", func);
2893869fb78SDag-Erling Smørgrav 		OUT(PAM_SYSTEM_ERR);
290f65b2180SDag-Erling Smørgrav 	}
2913869fb78SDag-Erling Smørgrav 	/* use poll() to watch the process and stdout / stderr */
2923869fb78SDag-Erling Smørgrav 	if (chout[1] >= 0)
2933869fb78SDag-Erling Smørgrav 		close(chout[1]);
2943869fb78SDag-Erling Smørgrav 	if (cherr[1] >= 0)
2953869fb78SDag-Erling Smørgrav 		close(cherr[1]);
2963869fb78SDag-Erling Smørgrav 	memset(pfd, 0, sizeof pfd);
2973869fb78SDag-Erling Smørgrav 	pfd[0].fd = pd;
2983869fb78SDag-Erling Smørgrav 	pfd[0].events = POLLHUP;
2993869fb78SDag-Erling Smørgrav 	nfds = 1;
3003869fb78SDag-Erling Smørgrav 	if (options->capture_stdout) {
3013869fb78SDag-Erling Smørgrav 		pfd[nfds].fd = chout[0];
3023869fb78SDag-Erling Smørgrav 		pfd[nfds].events = POLLIN|POLLERR|POLLHUP;
3033869fb78SDag-Erling Smørgrav 		nfds++;
3043869fb78SDag-Erling Smørgrav 	}
3053869fb78SDag-Erling Smørgrav 	if (options->capture_stderr) {
3063869fb78SDag-Erling Smørgrav 		pfd[nfds].fd = cherr[0];
3073869fb78SDag-Erling Smørgrav 		pfd[nfds].events = POLLIN|POLLERR|POLLHUP;
3083869fb78SDag-Erling Smørgrav 		nfds++;
3093869fb78SDag-Erling Smørgrav 	}
3103869fb78SDag-Erling Smørgrav 
3113869fb78SDag-Erling Smørgrav 	/* loop until the process exits */
3123869fb78SDag-Erling Smørgrav 	do {
3133869fb78SDag-Erling Smørgrav 		if (poll(pfd, nfds, INFTIM) < 0) {
3143869fb78SDag-Erling Smørgrav 			openpam_log(PAM_LOG_ERROR, "%s: poll(): %m", func);
3153869fb78SDag-Erling Smørgrav 			OUT(PAM_SYSTEM_ERR);
3163869fb78SDag-Erling Smørgrav 		}
3173869fb78SDag-Erling Smørgrav 		for (i = 1; i < nfds; ++i) {
3183869fb78SDag-Erling Smørgrav 			if ((pfd[i].revents & POLLIN) == 0)
3193869fb78SDag-Erling Smørgrav 				continue;
3203869fb78SDag-Erling Smørgrav 			if ((rlen = read(pfd[i].fd, buf, sizeof(buf) - 1)) < 0) {
3213869fb78SDag-Erling Smørgrav 				openpam_log(PAM_LOG_ERROR, "%s: read(): %m",
3223869fb78SDag-Erling Smørgrav 				    func);
3233869fb78SDag-Erling Smørgrav 				OUT(PAM_SYSTEM_ERR);
3243869fb78SDag-Erling Smørgrav 			} else if (rlen == 0) {
3253869fb78SDag-Erling Smørgrav 				continue;
3263869fb78SDag-Erling Smørgrav 			}
3273869fb78SDag-Erling Smørgrav 			buf[rlen] = '\0';
3283869fb78SDag-Erling Smørgrav 			(void)pam_prompt(pamh, pfd[i].fd == chout[0] ?
3293869fb78SDag-Erling Smørgrav 			    PAM_TEXT_INFO : PAM_ERROR_MSG, &resp, "%s", buf);
3303869fb78SDag-Erling Smørgrav 		}
3313869fb78SDag-Erling Smørgrav 	} while (pfd[0].revents == 0);
3323869fb78SDag-Erling Smørgrav 
3333869fb78SDag-Erling Smørgrav 	/* the child process has exited */
3347e3d5c1fSJean-Sébastien Pédron 	while (waitpid(pid, &status, 0) == -1) {
3357e3d5c1fSJean-Sébastien Pédron 		if (errno == EINTR)
3367e3d5c1fSJean-Sébastien Pédron 			continue;
3377e3d5c1fSJean-Sébastien Pédron 		openpam_log(PAM_LOG_ERROR, "%s: waitpid(): %m", func);
3383869fb78SDag-Erling Smørgrav 		OUT(PAM_SYSTEM_ERR);
339f65b2180SDag-Erling Smørgrav 	}
3403869fb78SDag-Erling Smørgrav 
3413869fb78SDag-Erling Smørgrav 	/* check exit code */
342f65b2180SDag-Erling Smørgrav 	if (WIFSIGNALED(status)) {
3437e3d5c1fSJean-Sébastien Pédron 		openpam_log(PAM_LOG_ERROR, "%s: %s caught signal %d%s",
3447e3d5c1fSJean-Sébastien Pédron 		    func, argv[0], WTERMSIG(status),
345f65b2180SDag-Erling Smørgrav 		    WCOREDUMP(status) ? " (core dumped)" : "");
3463869fb78SDag-Erling Smørgrav 		OUT(PAM_SERVICE_ERR);
347f65b2180SDag-Erling Smørgrav 	}
348f65b2180SDag-Erling Smørgrav 	if (!WIFEXITED(status)) {
3497e3d5c1fSJean-Sébastien Pédron 		openpam_log(PAM_LOG_ERROR, "%s: unknown status 0x%x",
3507e3d5c1fSJean-Sébastien Pédron 		    func, status);
3513869fb78SDag-Erling Smørgrav 		OUT(PAM_SERVICE_ERR);
352f65b2180SDag-Erling Smørgrav 	}
3537e3d5c1fSJean-Sébastien Pédron 
3543902d8a9SJean-Sébastien Pédron 	if (options->return_prog_exit_status) {
3557e3d5c1fSJean-Sébastien Pédron 		openpam_log(PAM_LOG_DEBUG,
3567e3d5c1fSJean-Sébastien Pédron 		    "%s: Use program exit status as return value: %d",
3577e3d5c1fSJean-Sébastien Pédron 		    func, WEXITSTATUS(status));
3583869fb78SDag-Erling Smørgrav 		OUT(WEXITSTATUS(status));
3597e3d5c1fSJean-Sébastien Pédron 	} else {
3603869fb78SDag-Erling Smørgrav 		OUT(WEXITSTATUS(status) == 0 ? PAM_SUCCESS : PAM_PERM_DENIED);
361f65b2180SDag-Erling Smørgrav 	}
3623869fb78SDag-Erling Smørgrav 	/* unreachable */
3633869fb78SDag-Erling Smørgrav out:
3643869fb78SDag-Erling Smørgrav 	serrno = errno;
3653869fb78SDag-Erling Smørgrav 	if (pd >= 0)
3663869fb78SDag-Erling Smørgrav 		close(pd);
3673869fb78SDag-Erling Smørgrav 	if (chout[0] >= 0)
3683869fb78SDag-Erling Smørgrav 		close(chout[0]);
3693869fb78SDag-Erling Smørgrav 	if (chout[1] >= 0)
3703869fb78SDag-Erling Smørgrav 		close(chout[1]);
3713869fb78SDag-Erling Smørgrav 	if (cherr[0] >= 0)
3723869fb78SDag-Erling Smørgrav 		close(cherr[0]);
3733869fb78SDag-Erling Smørgrav 	if (cherr[0] >= 0)
3743869fb78SDag-Erling Smørgrav 		close(cherr[1]);
3753869fb78SDag-Erling Smørgrav 	if (envlist != NULL)
3763869fb78SDag-Erling Smørgrav 		openpam_free_envlist(envlist);
3773869fb78SDag-Erling Smørgrav 	errno = serrno;
3783869fb78SDag-Erling Smørgrav 	return (pam_err);
379f65b2180SDag-Erling Smørgrav }
380f65b2180SDag-Erling Smørgrav 
381f65b2180SDag-Erling Smørgrav PAM_EXTERN int
382f65b2180SDag-Erling Smørgrav pam_sm_authenticate(pam_handle_t *pamh, int flags,
383f65b2180SDag-Erling Smørgrav     int argc, const char *argv[])
384f65b2180SDag-Erling Smørgrav {
3857e3d5c1fSJean-Sébastien Pédron 	int ret;
3863902d8a9SJean-Sébastien Pédron 	struct pe_opts options;
387f65b2180SDag-Erling Smørgrav 
3883902d8a9SJean-Sébastien Pédron 	ret = parse_options(__func__, &argc, &argv, &options);
3893902d8a9SJean-Sébastien Pédron 	if (ret != 0)
3903902d8a9SJean-Sébastien Pédron 		return (PAM_SERVICE_ERR);
3913902d8a9SJean-Sébastien Pédron 
3923902d8a9SJean-Sébastien Pédron 	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
3937e3d5c1fSJean-Sébastien Pédron 
3947e3d5c1fSJean-Sébastien Pédron 	/*
3957e3d5c1fSJean-Sébastien Pédron 	 * We must check that the program returned a valid code for this
3967e3d5c1fSJean-Sébastien Pédron 	 * function.
3977e3d5c1fSJean-Sébastien Pédron 	 */
3987e3d5c1fSJean-Sébastien Pédron 	switch (ret) {
3997e3d5c1fSJean-Sébastien Pédron 	case PAM_SUCCESS:
4007e3d5c1fSJean-Sébastien Pédron 	case PAM_ABORT:
4017e3d5c1fSJean-Sébastien Pédron 	case PAM_AUTHINFO_UNAVAIL:
4027e3d5c1fSJean-Sébastien Pédron 	case PAM_AUTH_ERR:
4037e3d5c1fSJean-Sébastien Pédron 	case PAM_BUF_ERR:
4047e3d5c1fSJean-Sébastien Pédron 	case PAM_CONV_ERR:
4057e3d5c1fSJean-Sébastien Pédron 	case PAM_CRED_INSUFFICIENT:
4067e3d5c1fSJean-Sébastien Pédron 	case PAM_IGNORE:
4077e3d5c1fSJean-Sébastien Pédron 	case PAM_MAXTRIES:
4087e3d5c1fSJean-Sébastien Pédron 	case PAM_PERM_DENIED:
4097e3d5c1fSJean-Sébastien Pédron 	case PAM_SERVICE_ERR:
4107e3d5c1fSJean-Sébastien Pédron 	case PAM_SYSTEM_ERR:
4117e3d5c1fSJean-Sébastien Pédron 	case PAM_USER_UNKNOWN:
4127e3d5c1fSJean-Sébastien Pédron 		break;
4137e3d5c1fSJean-Sébastien Pédron 	default:
4147e3d5c1fSJean-Sébastien Pédron 		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
4157e3d5c1fSJean-Sébastien Pédron 		    argv[0], ret);
4167e3d5c1fSJean-Sébastien Pédron 		ret = PAM_SERVICE_ERR;
4177e3d5c1fSJean-Sébastien Pédron 	}
4187e3d5c1fSJean-Sébastien Pédron 
4197e3d5c1fSJean-Sébastien Pédron 	return (ret);
420f65b2180SDag-Erling Smørgrav }
421f65b2180SDag-Erling Smørgrav 
422f65b2180SDag-Erling Smørgrav PAM_EXTERN int
423f65b2180SDag-Erling Smørgrav pam_sm_setcred(pam_handle_t *pamh, int flags,
424f65b2180SDag-Erling Smørgrav     int argc, const char *argv[])
425f65b2180SDag-Erling Smørgrav {
4267e3d5c1fSJean-Sébastien Pédron 	int ret;
4273902d8a9SJean-Sébastien Pédron 	struct pe_opts options;
428f65b2180SDag-Erling Smørgrav 
4293902d8a9SJean-Sébastien Pédron 	ret = parse_options(__func__, &argc, &argv, &options);
4303902d8a9SJean-Sébastien Pédron 	if (ret != 0)
4313902d8a9SJean-Sébastien Pédron 		return (PAM_SERVICE_ERR);
4323902d8a9SJean-Sébastien Pédron 
4333902d8a9SJean-Sébastien Pédron 	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
4347e3d5c1fSJean-Sébastien Pédron 
4357e3d5c1fSJean-Sébastien Pédron 	/*
4367e3d5c1fSJean-Sébastien Pédron 	 * We must check that the program returned a valid code for this
4377e3d5c1fSJean-Sébastien Pédron 	 * function.
4387e3d5c1fSJean-Sébastien Pédron 	 */
4397e3d5c1fSJean-Sébastien Pédron 	switch (ret) {
4407e3d5c1fSJean-Sébastien Pédron 	case PAM_SUCCESS:
4417e3d5c1fSJean-Sébastien Pédron 	case PAM_ABORT:
4427e3d5c1fSJean-Sébastien Pédron 	case PAM_BUF_ERR:
4437e3d5c1fSJean-Sébastien Pédron 	case PAM_CONV_ERR:
4447e3d5c1fSJean-Sébastien Pédron 	case PAM_CRED_ERR:
4457e3d5c1fSJean-Sébastien Pédron 	case PAM_CRED_EXPIRED:
4467e3d5c1fSJean-Sébastien Pédron 	case PAM_CRED_UNAVAIL:
4477e3d5c1fSJean-Sébastien Pédron 	case PAM_IGNORE:
4487e3d5c1fSJean-Sébastien Pédron 	case PAM_PERM_DENIED:
4497e3d5c1fSJean-Sébastien Pédron 	case PAM_SERVICE_ERR:
4507e3d5c1fSJean-Sébastien Pédron 	case PAM_SYSTEM_ERR:
4517e3d5c1fSJean-Sébastien Pédron 	case PAM_USER_UNKNOWN:
4527e3d5c1fSJean-Sébastien Pédron 		break;
4537e3d5c1fSJean-Sébastien Pédron 	default:
4547e3d5c1fSJean-Sébastien Pédron 		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
4557e3d5c1fSJean-Sébastien Pédron 		    argv[0], ret);
4567e3d5c1fSJean-Sébastien Pédron 		ret = PAM_SERVICE_ERR;
4577e3d5c1fSJean-Sébastien Pédron 	}
4587e3d5c1fSJean-Sébastien Pédron 
4597e3d5c1fSJean-Sébastien Pédron 	return (ret);
460f65b2180SDag-Erling Smørgrav }
461f65b2180SDag-Erling Smørgrav 
462f65b2180SDag-Erling Smørgrav PAM_EXTERN int
463f65b2180SDag-Erling Smørgrav pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
464f65b2180SDag-Erling Smørgrav     int argc, const char *argv[])
465f65b2180SDag-Erling Smørgrav {
4667e3d5c1fSJean-Sébastien Pédron 	int ret;
4673902d8a9SJean-Sébastien Pédron 	struct pe_opts options;
468f65b2180SDag-Erling Smørgrav 
4693902d8a9SJean-Sébastien Pédron 	ret = parse_options(__func__, &argc, &argv, &options);
4703902d8a9SJean-Sébastien Pédron 	if (ret != 0)
4713902d8a9SJean-Sébastien Pédron 		return (PAM_SERVICE_ERR);
4723902d8a9SJean-Sébastien Pédron 
4733902d8a9SJean-Sébastien Pédron 	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
4747e3d5c1fSJean-Sébastien Pédron 
4757e3d5c1fSJean-Sébastien Pédron 	/*
4767e3d5c1fSJean-Sébastien Pédron 	 * We must check that the program returned a valid code for this
4777e3d5c1fSJean-Sébastien Pédron 	 * function.
4787e3d5c1fSJean-Sébastien Pédron 	 */
4797e3d5c1fSJean-Sébastien Pédron 	switch (ret) {
4807e3d5c1fSJean-Sébastien Pédron 	case PAM_SUCCESS:
4817e3d5c1fSJean-Sébastien Pédron 	case PAM_ABORT:
4827e3d5c1fSJean-Sébastien Pédron 	case PAM_ACCT_EXPIRED:
4837e3d5c1fSJean-Sébastien Pédron 	case PAM_AUTH_ERR:
4847e3d5c1fSJean-Sébastien Pédron 	case PAM_BUF_ERR:
4857e3d5c1fSJean-Sébastien Pédron 	case PAM_CONV_ERR:
4867e3d5c1fSJean-Sébastien Pédron 	case PAM_IGNORE:
4877e3d5c1fSJean-Sébastien Pédron 	case PAM_NEW_AUTHTOK_REQD:
4887e3d5c1fSJean-Sébastien Pédron 	case PAM_PERM_DENIED:
4897e3d5c1fSJean-Sébastien Pédron 	case PAM_SERVICE_ERR:
4907e3d5c1fSJean-Sébastien Pédron 	case PAM_SYSTEM_ERR:
4917e3d5c1fSJean-Sébastien Pédron 	case PAM_USER_UNKNOWN:
4927e3d5c1fSJean-Sébastien Pédron 		break;
4937e3d5c1fSJean-Sébastien Pédron 	default:
4947e3d5c1fSJean-Sébastien Pédron 		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
4957e3d5c1fSJean-Sébastien Pédron 		    argv[0], ret);
4967e3d5c1fSJean-Sébastien Pédron 		ret = PAM_SERVICE_ERR;
4977e3d5c1fSJean-Sébastien Pédron 	}
4987e3d5c1fSJean-Sébastien Pédron 
4997e3d5c1fSJean-Sébastien Pédron 	return (ret);
500f65b2180SDag-Erling Smørgrav }
501f65b2180SDag-Erling Smørgrav 
502f65b2180SDag-Erling Smørgrav PAM_EXTERN int
503f65b2180SDag-Erling Smørgrav pam_sm_open_session(pam_handle_t *pamh, int flags,
504f65b2180SDag-Erling Smørgrav     int argc, const char *argv[])
505f65b2180SDag-Erling Smørgrav {
5067e3d5c1fSJean-Sébastien Pédron 	int ret;
5073902d8a9SJean-Sébastien Pédron 	struct pe_opts options;
508f65b2180SDag-Erling Smørgrav 
5093902d8a9SJean-Sébastien Pédron 	ret = parse_options(__func__, &argc, &argv, &options);
5103902d8a9SJean-Sébastien Pédron 	if (ret != 0)
5113902d8a9SJean-Sébastien Pédron 		return (PAM_SERVICE_ERR);
5123902d8a9SJean-Sébastien Pédron 
5133902d8a9SJean-Sébastien Pédron 	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
5147e3d5c1fSJean-Sébastien Pédron 
5157e3d5c1fSJean-Sébastien Pédron 	/*
5167e3d5c1fSJean-Sébastien Pédron 	 * We must check that the program returned a valid code for this
5177e3d5c1fSJean-Sébastien Pédron 	 * function.
5187e3d5c1fSJean-Sébastien Pédron 	 */
5197e3d5c1fSJean-Sébastien Pédron 	switch (ret) {
5207e3d5c1fSJean-Sébastien Pédron 	case PAM_SUCCESS:
5217e3d5c1fSJean-Sébastien Pédron 	case PAM_ABORT:
5227e3d5c1fSJean-Sébastien Pédron 	case PAM_BUF_ERR:
5237e3d5c1fSJean-Sébastien Pédron 	case PAM_CONV_ERR:
5247e3d5c1fSJean-Sébastien Pédron 	case PAM_IGNORE:
5257e3d5c1fSJean-Sébastien Pédron 	case PAM_PERM_DENIED:
5267e3d5c1fSJean-Sébastien Pédron 	case PAM_SERVICE_ERR:
5277e3d5c1fSJean-Sébastien Pédron 	case PAM_SESSION_ERR:
5287e3d5c1fSJean-Sébastien Pédron 	case PAM_SYSTEM_ERR:
5297e3d5c1fSJean-Sébastien Pédron 		break;
5307e3d5c1fSJean-Sébastien Pédron 	default:
5317e3d5c1fSJean-Sébastien Pédron 		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
5327e3d5c1fSJean-Sébastien Pédron 		    argv[0], ret);
5337e3d5c1fSJean-Sébastien Pédron 		ret = PAM_SERVICE_ERR;
5347e3d5c1fSJean-Sébastien Pédron 	}
5357e3d5c1fSJean-Sébastien Pédron 
5367e3d5c1fSJean-Sébastien Pédron 	return (ret);
537f65b2180SDag-Erling Smørgrav }
538f65b2180SDag-Erling Smørgrav 
539f65b2180SDag-Erling Smørgrav PAM_EXTERN int
540f65b2180SDag-Erling Smørgrav pam_sm_close_session(pam_handle_t *pamh, int flags,
541f65b2180SDag-Erling Smørgrav     int argc, const char *argv[])
542f65b2180SDag-Erling Smørgrav {
5437e3d5c1fSJean-Sébastien Pédron 	int ret;
5443902d8a9SJean-Sébastien Pédron 	struct pe_opts options;
545f65b2180SDag-Erling Smørgrav 
5463902d8a9SJean-Sébastien Pédron 	ret = parse_options(__func__, &argc, &argv, &options);
5473902d8a9SJean-Sébastien Pédron 	if (ret != 0)
5483902d8a9SJean-Sébastien Pédron 		return (PAM_SERVICE_ERR);
5493902d8a9SJean-Sébastien Pédron 
5503902d8a9SJean-Sébastien Pédron 	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
5517e3d5c1fSJean-Sébastien Pédron 
5527e3d5c1fSJean-Sébastien Pédron 	/*
5537e3d5c1fSJean-Sébastien Pédron 	 * We must check that the program returned a valid code for this
5547e3d5c1fSJean-Sébastien Pédron 	 * function.
5557e3d5c1fSJean-Sébastien Pédron 	 */
5567e3d5c1fSJean-Sébastien Pédron 	switch (ret) {
5577e3d5c1fSJean-Sébastien Pédron 	case PAM_SUCCESS:
5587e3d5c1fSJean-Sébastien Pédron 	case PAM_ABORT:
5597e3d5c1fSJean-Sébastien Pédron 	case PAM_BUF_ERR:
5607e3d5c1fSJean-Sébastien Pédron 	case PAM_CONV_ERR:
5617e3d5c1fSJean-Sébastien Pédron 	case PAM_IGNORE:
5627e3d5c1fSJean-Sébastien Pédron 	case PAM_PERM_DENIED:
5637e3d5c1fSJean-Sébastien Pédron 	case PAM_SERVICE_ERR:
5647e3d5c1fSJean-Sébastien Pédron 	case PAM_SESSION_ERR:
5657e3d5c1fSJean-Sébastien Pédron 	case PAM_SYSTEM_ERR:
5667e3d5c1fSJean-Sébastien Pédron 		break;
5677e3d5c1fSJean-Sébastien Pédron 	default:
5687e3d5c1fSJean-Sébastien Pédron 		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
5697e3d5c1fSJean-Sébastien Pédron 		    argv[0], ret);
5707e3d5c1fSJean-Sébastien Pédron 		ret = PAM_SERVICE_ERR;
5717e3d5c1fSJean-Sébastien Pédron 	}
5727e3d5c1fSJean-Sébastien Pédron 
5737e3d5c1fSJean-Sébastien Pédron 	return (ret);
574f65b2180SDag-Erling Smørgrav }
575f65b2180SDag-Erling Smørgrav 
576f65b2180SDag-Erling Smørgrav PAM_EXTERN int
577f65b2180SDag-Erling Smørgrav pam_sm_chauthtok(pam_handle_t *pamh, int flags,
578f65b2180SDag-Erling Smørgrav     int argc, const char *argv[])
579f65b2180SDag-Erling Smørgrav {
5807e3d5c1fSJean-Sébastien Pédron 	int ret;
5813902d8a9SJean-Sébastien Pédron 	struct pe_opts options;
582f65b2180SDag-Erling Smørgrav 
5833902d8a9SJean-Sébastien Pédron 	ret = parse_options(__func__, &argc, &argv, &options);
5843902d8a9SJean-Sébastien Pédron 	if (ret != 0)
5853902d8a9SJean-Sébastien Pédron 		return (PAM_SERVICE_ERR);
5863902d8a9SJean-Sébastien Pédron 
5873902d8a9SJean-Sébastien Pédron 	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
5887e3d5c1fSJean-Sébastien Pédron 
5897e3d5c1fSJean-Sébastien Pédron 	/*
5907e3d5c1fSJean-Sébastien Pédron 	 * We must check that the program returned a valid code for this
5917e3d5c1fSJean-Sébastien Pédron 	 * function.
5927e3d5c1fSJean-Sébastien Pédron 	 */
5937e3d5c1fSJean-Sébastien Pédron 	switch (ret) {
5947e3d5c1fSJean-Sébastien Pédron 	case PAM_SUCCESS:
5957e3d5c1fSJean-Sébastien Pédron 	case PAM_ABORT:
5967e3d5c1fSJean-Sébastien Pédron 	case PAM_AUTHTOK_DISABLE_AGING:
5977e3d5c1fSJean-Sébastien Pédron 	case PAM_AUTHTOK_ERR:
5987e3d5c1fSJean-Sébastien Pédron 	case PAM_AUTHTOK_LOCK_BUSY:
5997e3d5c1fSJean-Sébastien Pédron 	case PAM_AUTHTOK_RECOVERY_ERR:
6007e3d5c1fSJean-Sébastien Pédron 	case PAM_BUF_ERR:
6017e3d5c1fSJean-Sébastien Pédron 	case PAM_CONV_ERR:
6027e3d5c1fSJean-Sébastien Pédron 	case PAM_IGNORE:
6037e3d5c1fSJean-Sébastien Pédron 	case PAM_PERM_DENIED:
6047e3d5c1fSJean-Sébastien Pédron 	case PAM_SERVICE_ERR:
6057e3d5c1fSJean-Sébastien Pédron 	case PAM_SYSTEM_ERR:
6067e3d5c1fSJean-Sébastien Pédron 	case PAM_TRY_AGAIN:
6077e3d5c1fSJean-Sébastien Pédron 		break;
6087e3d5c1fSJean-Sébastien Pédron 	default:
6097e3d5c1fSJean-Sébastien Pédron 		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
6107e3d5c1fSJean-Sébastien Pédron 		    argv[0], ret);
6117e3d5c1fSJean-Sébastien Pédron 		ret = PAM_SERVICE_ERR;
6127e3d5c1fSJean-Sébastien Pédron 	}
6137e3d5c1fSJean-Sébastien Pédron 
6147e3d5c1fSJean-Sébastien Pédron 	return (ret);
615f65b2180SDag-Erling Smørgrav }
616f65b2180SDag-Erling Smørgrav 
617f65b2180SDag-Erling Smørgrav PAM_MODULE_ENTRY("pam_exec");
618