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