1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 2001,2003 Networks Associates Technology, Inc.
5 * Copyright (c) 2017-2019 Dag-Erling Sm��rgrav
6 * Copyright (c) 2018 Thomas Munro
7 * All rights reserved.
8 *
9 * This software was developed for the FreeBSD Project by ThinkSec AS and
10 * NAI Labs, the Security Research Division of Network Associates, Inc.
11 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
12 * DARPA CHATS research program.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 *    notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in the
21 *    documentation and/or other materials provided with the distribution.
22 * 3. The name of the author may not be used to endorse or promote
23 *    products derived from this software without specific prior written
24 *    permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39#include <sys/types.h>
40#include <sys/poll.h>
41#include <sys/procdesc.h>
42#include <sys/wait.h>
43
44#include <errno.h>
45#include <fcntl.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <unistd.h>
50
51#include <security/pam_appl.h>
52#include <security/pam_modules.h>
53#include <security/openpam.h>
54
55#define PAM_ITEM_ENV(n) { (n), #n }
56static struct {
57	int item;
58	const char *name;
59} pam_item_env[] = {
60	PAM_ITEM_ENV(PAM_SERVICE),
61	PAM_ITEM_ENV(PAM_USER),
62	PAM_ITEM_ENV(PAM_TTY),
63	PAM_ITEM_ENV(PAM_RHOST),
64	PAM_ITEM_ENV(PAM_RUSER),
65};
66#define NUM_PAM_ITEM_ENV (sizeof(pam_item_env) / sizeof(pam_item_env[0]))
67
68#define PAM_ERR_ENV_X(str, num) str "=" #num
69#define PAM_ERR_ENV(pam_err) PAM_ERR_ENV_X(#pam_err, pam_err)
70static const char *pam_err_env[] = {
71	PAM_ERR_ENV(PAM_SUCCESS),
72	PAM_ERR_ENV(PAM_OPEN_ERR),
73	PAM_ERR_ENV(PAM_SYMBOL_ERR),
74	PAM_ERR_ENV(PAM_SERVICE_ERR),
75	PAM_ERR_ENV(PAM_SYSTEM_ERR),
76	PAM_ERR_ENV(PAM_BUF_ERR),
77	PAM_ERR_ENV(PAM_CONV_ERR),
78	PAM_ERR_ENV(PAM_PERM_DENIED),
79	PAM_ERR_ENV(PAM_MAXTRIES),
80	PAM_ERR_ENV(PAM_AUTH_ERR),
81	PAM_ERR_ENV(PAM_NEW_AUTHTOK_REQD),
82	PAM_ERR_ENV(PAM_CRED_INSUFFICIENT),
83	PAM_ERR_ENV(PAM_AUTHINFO_UNAVAIL),
84	PAM_ERR_ENV(PAM_USER_UNKNOWN),
85	PAM_ERR_ENV(PAM_CRED_UNAVAIL),
86	PAM_ERR_ENV(PAM_CRED_EXPIRED),
87	PAM_ERR_ENV(PAM_CRED_ERR),
88	PAM_ERR_ENV(PAM_ACCT_EXPIRED),
89	PAM_ERR_ENV(PAM_AUTHTOK_EXPIRED),
90	PAM_ERR_ENV(PAM_SESSION_ERR),
91	PAM_ERR_ENV(PAM_AUTHTOK_ERR),
92	PAM_ERR_ENV(PAM_AUTHTOK_RECOVERY_ERR),
93	PAM_ERR_ENV(PAM_AUTHTOK_LOCK_BUSY),
94	PAM_ERR_ENV(PAM_AUTHTOK_DISABLE_AGING),
95	PAM_ERR_ENV(PAM_NO_MODULE_DATA),
96	PAM_ERR_ENV(PAM_IGNORE),
97	PAM_ERR_ENV(PAM_ABORT),
98	PAM_ERR_ENV(PAM_TRY_AGAIN),
99	PAM_ERR_ENV(PAM_MODULE_UNKNOWN),
100	PAM_ERR_ENV(PAM_DOMAIN_UNKNOWN),
101	PAM_ERR_ENV(PAM_NUM_ERR),
102};
103#define NUM_PAM_ERR_ENV (sizeof(pam_err_env) / sizeof(pam_err_env[0]))
104
105struct pe_opts {
106	int	return_prog_exit_status;
107	int	capture_stdout;
108	int	capture_stderr;
109	int	expose_authtok;
110	int	use_first_pass;
111};
112
113static int
114parse_options(const char *func, int *argc, const char **argv[],
115    struct pe_opts *options)
116{
117	int i;
118
119	/*
120	 * Parse options:
121	 *   return_prog_exit_status:
122	 *     use the program exit status as the return code of pam_exec
123	 *   --:
124	 *     stop options parsing; what follows is the command to execute
125	 */
126	memset(options, 0, sizeof(*options));
127
128	for (i = 0; i < *argc; ++i) {
129		if (strcmp((*argv)[i], "debug") == 0 ||
130		    strcmp((*argv)[i], "no_warn") == 0) {
131			/* ignore */
132		} else if (strcmp((*argv)[i], "capture_stdout") == 0) {
133			options->capture_stdout = 1;
134		} else if (strcmp((*argv)[i], "capture_stderr") == 0) {
135			options->capture_stderr = 1;
136		} else if (strcmp((*argv)[i], "return_prog_exit_status") == 0) {
137			options->return_prog_exit_status = 1;
138		} else if (strcmp((*argv)[i], "expose_authtok") == 0) {
139			options->expose_authtok = 1;
140		} else if (strcmp((*argv)[i], "use_first_pass") == 0) {
141			options->use_first_pass = 1;
142		} else {
143			if (strcmp((*argv)[i], "--") == 0) {
144				(*argc)--;
145				(*argv)++;
146			}
147			break;
148		}
149		openpam_log(PAM_LOG_DEBUG, "%s: option \"%s\" enabled",
150		    func, (*argv)[i]);
151	}
152
153	(*argc) -= i;
154	(*argv) += i;
155
156	return (0);
157}
158
159static int
160_pam_exec(pam_handle_t *pamh,
161    const char *func, int flags __unused, int argc, const char *argv[],
162    struct pe_opts *options)
163{
164	char buf[PAM_MAX_MSG_SIZE];
165	struct pollfd pfd[4];
166	const void *item;
167	char **envlist, *envstr, *resp, **tmp;
168	ssize_t rlen, wlen;
169	int envlen, extralen, i;
170	int pam_err, serrno, status;
171	int chin[2], chout[2], cherr[2], pd;
172	nfds_t nfds, nreadfds;
173	pid_t pid;
174	const char *authtok;
175	size_t authtok_size;
176	int rc;
177
178	pd = -1;
179	pid = 0;
180	chin[0] = chin[1] = chout[0] = chout[1] = cherr[0] = cherr[1] = -1;
181	envlist = NULL;
182
183#define OUT(ret) do { pam_err = (ret); goto out; } while (0)
184
185	/* Check there's a program name left after parsing options. */
186	if (argc < 1) {
187		openpam_log(PAM_LOG_ERROR, "%s: No program specified: aborting",
188		    func);
189		OUT(PAM_SERVICE_ERR);
190	}
191
192	/*
193	 * Set up the child's environment list.  It consists of the PAM
194	 * environment, a few hand-picked PAM items, the name of the
195	 * service function, and if return_prog_exit_status is set, the
196	 * numerical values of all PAM error codes.
197	 */
198
199	/* compute the final size of the environment. */
200	envlist = pam_getenvlist(pamh);
201	for (envlen = 0; envlist[envlen] != NULL; ++envlen)
202		/* nothing */ ;
203	extralen = NUM_PAM_ITEM_ENV + 1;
204	if (options->return_prog_exit_status)
205		extralen += NUM_PAM_ERR_ENV;
206	tmp = reallocarray(envlist, envlen + extralen + 1, sizeof(*envlist));
207	openpam_log(PAM_LOG_DEBUG, "envlen = %d extralen = %d tmp = %p",
208	    envlen, extralen, tmp);
209	if (tmp == NULL)
210		OUT(PAM_BUF_ERR);
211	envlist = tmp;
212	extralen += envlen;
213
214	/* copy selected PAM items to the environment */
215	for (i = 0; i < NUM_PAM_ITEM_ENV; ++i) {
216		pam_err = pam_get_item(pamh, pam_item_env[i].item, &item);
217		if (pam_err != PAM_SUCCESS || item == NULL)
218			continue;
219		if (asprintf(&envstr, "%s=%s", pam_item_env[i].name,
220		    (const char *)item) < 0)
221			OUT(PAM_BUF_ERR);
222		envlist[envlen++] = envstr;
223		envlist[envlen] = NULL;
224		openpam_log(PAM_LOG_DEBUG, "setenv %s", envstr);
225	}
226
227	/* add the name of the service function to the environment */
228	if (asprintf(&envstr, "PAM_SM_FUNC=%s", func) < 0)
229		OUT(PAM_BUF_ERR);
230	envlist[envlen++] = envstr;
231	envlist[envlen] = NULL;
232
233	/* add the PAM error codes to the environment. */
234	if (options->return_prog_exit_status) {
235		for (i = 0; i < (int)NUM_PAM_ERR_ENV; ++i) {
236			if ((envstr = strdup(pam_err_env[i])) == NULL)
237				OUT(PAM_BUF_ERR);
238			envlist[envlen++] = envstr;
239			envlist[envlen] = NULL;
240		}
241	}
242
243	openpam_log(PAM_LOG_DEBUG, "envlen = %d extralen = %d envlist = %p",
244	    envlen, extralen, envlist);
245
246	/* set up pipe and get authtok if requested */
247	if (options->expose_authtok) {
248		if (pipe(chin) != 0) {
249			openpam_log(PAM_LOG_ERROR, "%s: pipe(): %m", func);
250			OUT(PAM_SYSTEM_ERR);
251		}
252		if (fcntl(chin[1], F_SETFL, O_NONBLOCK)) {
253			openpam_log(PAM_LOG_ERROR, "%s: fcntl(): %m", func);
254			OUT(PAM_SYSTEM_ERR);
255		}
256		if (options->use_first_pass ||
257		    strcmp(func, "pam_sm_setcred") == 0) {
258			/* don't prompt, only expose existing token */
259			rc = pam_get_item(pamh, PAM_AUTHTOK, &item);
260			authtok = item;
261			if (authtok == NULL && rc == PAM_SUCCESS) {
262				openpam_log(PAM_LOG_ERROR,
263				    "%s: pam_get_authtok(): %s",
264				    func, "authentication token not available");
265				OUT(PAM_SYSTEM_ERR);
266			}
267
268		} else {
269			rc = pam_get_authtok(pamh, PAM_AUTHTOK, &authtok, NULL);
270		}
271		if (rc == PAM_SUCCESS) {
272			/* We include the trailing null terminator. */
273			authtok_size = strlen(authtok) + 1;
274		} else {
275			openpam_log(PAM_LOG_ERROR, "%s: pam_get_authtok(): %s",
276			    func, pam_strerror(pamh, rc));
277			OUT(PAM_SYSTEM_ERR);
278		}
279	}
280	/* set up pipes if capture was requested */
281	if (options->capture_stdout) {
282		if (pipe(chout) != 0) {
283			openpam_log(PAM_LOG_ERROR, "%s: pipe(): %m", func);
284			OUT(PAM_SYSTEM_ERR);
285		}
286		if (fcntl(chout[0], F_SETFL, O_NONBLOCK) != 0) {
287			openpam_log(PAM_LOG_ERROR, "%s: fcntl(): %m", func);
288			OUT(PAM_SYSTEM_ERR);
289		}
290	} else {
291		if ((chout[1] = open("/dev/null", O_RDWR)) < 0) {
292			openpam_log(PAM_LOG_ERROR, "%s: /dev/null: %m", func);
293			OUT(PAM_SYSTEM_ERR);
294		}
295	}
296	if (options->capture_stderr) {
297		if (pipe(cherr) != 0) {
298			openpam_log(PAM_LOG_ERROR, "%s: pipe(): %m", func);
299			OUT(PAM_SYSTEM_ERR);
300		}
301		if (fcntl(cherr[0], F_SETFL, O_NONBLOCK) != 0) {
302			openpam_log(PAM_LOG_ERROR, "%s: fcntl(): %m", func);
303			OUT(PAM_SYSTEM_ERR);
304		}
305	} else {
306		if ((cherr[1] = open("/dev/null", O_RDWR)) < 0) {
307			openpam_log(PAM_LOG_ERROR, "%s: /dev/null: %m", func);
308			OUT(PAM_SYSTEM_ERR);
309		}
310	}
311
312	if ((pid = pdfork(&pd, 0)) == 0) {
313		/* child */
314		if ((chin[1] >= 0 && close(chin[1]) != 0) ||
315			(chout[0] >= 0 && close(chout[0]) != 0) ||
316		    (cherr[0] >= 0 && close(cherr[0]) != 0)) {
317			openpam_log(PAM_LOG_ERROR, "%s: close(): %m", func);
318		} else if (chin[0] >= 0 &&
319			dup2(chin[0], STDIN_FILENO) != STDIN_FILENO) {
320			openpam_log(PAM_LOG_ERROR, "%s: dup2(): %m", func);
321		} else if (dup2(chout[1], STDOUT_FILENO) != STDOUT_FILENO ||
322		    dup2(cherr[1], STDERR_FILENO) != STDERR_FILENO) {
323			openpam_log(PAM_LOG_ERROR, "%s: dup2(): %m", func);
324		} else {
325			execve(argv[0], (char * const *)argv,
326			    (char * const *)envlist);
327			openpam_log(PAM_LOG_ERROR, "%s: execve(%s): %m",
328			    func, argv[0]);
329		}
330		_exit(1);
331	}
332	/* parent */
333	if (pid == -1) {
334		openpam_log(PAM_LOG_ERROR, "%s: pdfork(): %m", func);
335		OUT(PAM_SYSTEM_ERR);
336	}
337	/* use poll() to watch the process and stdin / stdout / stderr */
338	if (chin[0] >= 0)
339		close(chin[0]);
340	if (chout[1] >= 0)
341		close(chout[1]);
342	if (cherr[1] >= 0)
343		close(cherr[1]);
344	memset(pfd, 0, sizeof pfd);
345	pfd[0].fd = pd;
346	pfd[0].events = POLLHUP;
347	nfds = 1;
348	nreadfds = 0;
349	if (options->capture_stdout) {
350		pfd[nfds].fd = chout[0];
351		pfd[nfds].events = POLLIN|POLLERR|POLLHUP;
352		nfds++;
353		nreadfds++;
354	}
355	if (options->capture_stderr) {
356		pfd[nfds].fd = cherr[0];
357		pfd[nfds].events = POLLIN|POLLERR|POLLHUP;
358		nfds++;
359		nreadfds++;
360	}
361	if (options->expose_authtok) {
362		pfd[nfds].fd = chin[1];
363		pfd[nfds].events = POLLOUT|POLLERR|POLLHUP;
364		nfds++;
365	}
366
367	/* loop until the process exits */
368	do {
369		if (poll(pfd, nfds, INFTIM) < 0) {
370			openpam_log(PAM_LOG_ERROR, "%s: poll(): %m", func);
371			OUT(PAM_SYSTEM_ERR);
372		}
373		/* are the stderr / stdout pipes ready for reading? */
374		for (i = 1; i < 1 + nreadfds; ++i) {
375			if ((pfd[i].revents & POLLIN) == 0)
376				continue;
377			if ((rlen = read(pfd[i].fd, buf, sizeof(buf) - 1)) < 0) {
378				openpam_log(PAM_LOG_ERROR, "%s: read(): %m",
379				    func);
380				OUT(PAM_SYSTEM_ERR);
381			} else if (rlen == 0) {
382				continue;
383			}
384			buf[rlen] = '\0';
385			(void)pam_prompt(pamh, pfd[i].fd == chout[0] ?
386			    PAM_TEXT_INFO : PAM_ERROR_MSG, &resp, "%s", buf);
387		}
388		/* is the stdin pipe ready for writing? */
389		if (options->expose_authtok && authtok_size > 0 &&
390			(pfd[nfds - 1].revents & POLLOUT) != 0) {
391			if ((wlen = write(chin[1], authtok, authtok_size)) < 0) {
392				if (errno == EAGAIN)
393					continue;
394				openpam_log(PAM_LOG_ERROR, "%s: write(): %m",
395				    func);
396				OUT(PAM_SYSTEM_ERR);
397			} else {
398				authtok += wlen;
399				authtok_size -= wlen;
400				if (authtok_size == 0) {
401					/* finished writing; close and forget the pipe */
402					close(chin[1]);
403					chin[1] = -1;
404					nfds--;
405				}
406			}
407		}
408	} while (pfd[0].revents == 0);
409
410	/* the child process has exited */
411	while (waitpid(pid, &status, 0) == -1) {
412		if (errno == EINTR)
413			continue;
414		openpam_log(PAM_LOG_ERROR, "%s: waitpid(): %m", func);
415		OUT(PAM_SYSTEM_ERR);
416	}
417
418	/* check exit code */
419	if (WIFSIGNALED(status)) {
420		openpam_log(PAM_LOG_ERROR, "%s: %s caught signal %d%s",
421		    func, argv[0], WTERMSIG(status),
422		    WCOREDUMP(status) ? " (core dumped)" : "");
423		OUT(PAM_SERVICE_ERR);
424	}
425	if (!WIFEXITED(status)) {
426		openpam_log(PAM_LOG_ERROR, "%s: unknown status 0x%x",
427		    func, status);
428		OUT(PAM_SERVICE_ERR);
429	}
430
431	if (options->return_prog_exit_status) {
432		openpam_log(PAM_LOG_DEBUG,
433		    "%s: Use program exit status as return value: %d",
434		    func, WEXITSTATUS(status));
435		OUT(WEXITSTATUS(status));
436	} else {
437		OUT(WEXITSTATUS(status) == 0 ? PAM_SUCCESS : PAM_PERM_DENIED);
438	}
439	/* unreachable */
440out:
441	serrno = errno;
442	if (pd >= 0)
443		close(pd);
444	if (chin[0] >= 0)
445		close(chin[0]);
446	if (chin[1] >= 0)
447		close(chin[1]);
448	if (chout[0] >= 0)
449		close(chout[0]);
450	if (chout[1] >= 0)
451		close(chout[1]);
452	if (cherr[0] >= 0)
453		close(cherr[0]);
454	if (cherr[0] >= 0)
455		close(cherr[1]);
456	if (envlist != NULL)
457		openpam_free_envlist(envlist);
458	errno = serrno;
459	return (pam_err);
460}
461
462PAM_EXTERN int
463pam_sm_authenticate(pam_handle_t *pamh, int flags,
464    int argc, const char *argv[])
465{
466	int ret;
467	struct pe_opts options;
468
469	ret = parse_options(__func__, &argc, &argv, &options);
470	if (ret != 0)
471		return (PAM_SERVICE_ERR);
472
473	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
474
475	/*
476	 * We must check that the program returned a valid code for this
477	 * function.
478	 */
479	switch (ret) {
480	case PAM_SUCCESS:
481	case PAM_ABORT:
482	case PAM_AUTHINFO_UNAVAIL:
483	case PAM_AUTH_ERR:
484	case PAM_BUF_ERR:
485	case PAM_CONV_ERR:
486	case PAM_CRED_INSUFFICIENT:
487	case PAM_IGNORE:
488	case PAM_MAXTRIES:
489	case PAM_PERM_DENIED:
490	case PAM_SERVICE_ERR:
491	case PAM_SYSTEM_ERR:
492	case PAM_USER_UNKNOWN:
493		break;
494	default:
495		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
496		    argv[0], ret);
497		ret = PAM_SERVICE_ERR;
498	}
499
500	return (ret);
501}
502
503PAM_EXTERN int
504pam_sm_setcred(pam_handle_t *pamh, int flags,
505    int argc, const char *argv[])
506{
507	int ret;
508	struct pe_opts options;
509
510	ret = parse_options(__func__, &argc, &argv, &options);
511	if (ret != 0)
512		return (PAM_SERVICE_ERR);
513
514	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
515
516	/*
517	 * We must check that the program returned a valid code for this
518	 * function.
519	 */
520	switch (ret) {
521	case PAM_SUCCESS:
522	case PAM_ABORT:
523	case PAM_BUF_ERR:
524	case PAM_CONV_ERR:
525	case PAM_CRED_ERR:
526	case PAM_CRED_EXPIRED:
527	case PAM_CRED_UNAVAIL:
528	case PAM_IGNORE:
529	case PAM_PERM_DENIED:
530	case PAM_SERVICE_ERR:
531	case PAM_SYSTEM_ERR:
532	case PAM_USER_UNKNOWN:
533		break;
534	default:
535		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
536		    argv[0], ret);
537		ret = PAM_SERVICE_ERR;
538	}
539
540	return (ret);
541}
542
543PAM_EXTERN int
544pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
545    int argc, const char *argv[])
546{
547	int ret;
548	struct pe_opts options;
549
550	ret = parse_options(__func__, &argc, &argv, &options);
551	if (ret != 0)
552		return (PAM_SERVICE_ERR);
553
554	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
555
556	/*
557	 * We must check that the program returned a valid code for this
558	 * function.
559	 */
560	switch (ret) {
561	case PAM_SUCCESS:
562	case PAM_ABORT:
563	case PAM_ACCT_EXPIRED:
564	case PAM_AUTH_ERR:
565	case PAM_BUF_ERR:
566	case PAM_CONV_ERR:
567	case PAM_IGNORE:
568	case PAM_NEW_AUTHTOK_REQD:
569	case PAM_PERM_DENIED:
570	case PAM_SERVICE_ERR:
571	case PAM_SYSTEM_ERR:
572	case PAM_USER_UNKNOWN:
573		break;
574	default:
575		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
576		    argv[0], ret);
577		ret = PAM_SERVICE_ERR;
578	}
579
580	return (ret);
581}
582
583PAM_EXTERN int
584pam_sm_open_session(pam_handle_t *pamh, int flags,
585    int argc, const char *argv[])
586{
587	int ret;
588	struct pe_opts options;
589
590	ret = parse_options(__func__, &argc, &argv, &options);
591	if (ret != 0)
592		return (PAM_SERVICE_ERR);
593
594	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
595
596	/*
597	 * We must check that the program returned a valid code for this
598	 * function.
599	 */
600	switch (ret) {
601	case PAM_SUCCESS:
602	case PAM_ABORT:
603	case PAM_BUF_ERR:
604	case PAM_CONV_ERR:
605	case PAM_IGNORE:
606	case PAM_PERM_DENIED:
607	case PAM_SERVICE_ERR:
608	case PAM_SESSION_ERR:
609	case PAM_SYSTEM_ERR:
610		break;
611	default:
612		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
613		    argv[0], ret);
614		ret = PAM_SERVICE_ERR;
615	}
616
617	return (ret);
618}
619
620PAM_EXTERN int
621pam_sm_close_session(pam_handle_t *pamh, int flags,
622    int argc, const char *argv[])
623{
624	int ret;
625	struct pe_opts options;
626
627	ret = parse_options(__func__, &argc, &argv, &options);
628	if (ret != 0)
629		return (PAM_SERVICE_ERR);
630
631	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
632
633	/*
634	 * We must check that the program returned a valid code for this
635	 * function.
636	 */
637	switch (ret) {
638	case PAM_SUCCESS:
639	case PAM_ABORT:
640	case PAM_BUF_ERR:
641	case PAM_CONV_ERR:
642	case PAM_IGNORE:
643	case PAM_PERM_DENIED:
644	case PAM_SERVICE_ERR:
645	case PAM_SESSION_ERR:
646	case PAM_SYSTEM_ERR:
647		break;
648	default:
649		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
650		    argv[0], ret);
651		ret = PAM_SERVICE_ERR;
652	}
653
654	return (ret);
655}
656
657PAM_EXTERN int
658pam_sm_chauthtok(pam_handle_t *pamh, int flags,
659    int argc, const char *argv[])
660{
661	int ret;
662	struct pe_opts options;
663
664	ret = parse_options(__func__, &argc, &argv, &options);
665	if (ret != 0)
666		return (PAM_SERVICE_ERR);
667
668	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
669
670	/*
671	 * We must check that the program returned a valid code for this
672	 * function.
673	 */
674	switch (ret) {
675	case PAM_SUCCESS:
676	case PAM_ABORT:
677	case PAM_AUTHTOK_DISABLE_AGING:
678	case PAM_AUTHTOK_ERR:
679	case PAM_AUTHTOK_LOCK_BUSY:
680	case PAM_AUTHTOK_RECOVERY_ERR:
681	case PAM_BUF_ERR:
682	case PAM_CONV_ERR:
683	case PAM_IGNORE:
684	case PAM_PERM_DENIED:
685	case PAM_SERVICE_ERR:
686	case PAM_SYSTEM_ERR:
687	case PAM_TRY_AGAIN:
688		break;
689	default:
690		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
691		    argv[0], ret);
692		ret = PAM_SERVICE_ERR;
693	}
694
695	return (ret);
696}
697
698PAM_MODULE_ENTRY("pam_exec");
699