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,
223		    (const char *)item) < 0)
224			OUT(PAM_BUF_ERR);
225		envlist[envlen++] = envstr;
226		envlist[envlen] = NULL;
227		openpam_log(PAM_LOG_DEBUG, "setenv %s", envstr);
228	}
229
230	/* add the name of the service function to the environment */
231	if (asprintf(&envstr, "PAM_SM_FUNC=%s", func) < 0)
232		OUT(PAM_BUF_ERR);
233	envlist[envlen++] = envstr;
234	envlist[envlen] = NULL;
235
236	/* add the PAM error codes to the environment. */
237	if (options->return_prog_exit_status) {
238		for (i = 0; i < (int)NUM_PAM_ERR_ENV; ++i) {
239			if ((envstr = strdup(pam_err_env[i])) == NULL)
240				OUT(PAM_BUF_ERR);
241			envlist[envlen++] = envstr;
242			envlist[envlen] = NULL;
243		}
244	}
245
246	openpam_log(PAM_LOG_DEBUG, "envlen = %d extralen = %d envlist = %p",
247	    envlen, extralen, envlist);
248
249	/* set up pipe and get authtok if requested */
250	if (options->expose_authtok) {
251		if (pipe(chin) != 0) {
252			openpam_log(PAM_LOG_ERROR, "%s: pipe(): %m", func);
253			OUT(PAM_SYSTEM_ERR);
254		}
255		if (fcntl(chin[1], F_SETFL, O_NONBLOCK)) {
256			openpam_log(PAM_LOG_ERROR, "%s: fcntl(): %m", func);
257			OUT(PAM_SYSTEM_ERR);
258		}
259		if (options->use_first_pass ||
260		    strcmp(func, "pam_sm_setcred") == 0) {
261			/* don't prompt, only expose existing token */
262			rc = pam_get_item(pamh, PAM_AUTHTOK, &item);
263			authtok = item;
264		} else {
265			rc = pam_get_authtok(pamh, PAM_AUTHTOK, &authtok, NULL);
266		}
267		if (rc == PAM_SUCCESS) {
268			/* We include the trailing null terminator. */
269			authtok_size = strlen(authtok) + 1;
270		} else {
271			openpam_log(PAM_LOG_ERROR, "%s: pam_get_authtok(): %s",
272			    func, pam_strerror(pamh, rc));
273			OUT(PAM_SYSTEM_ERR);
274		}
275	}
276	/* set up pipes if capture was requested */
277	if (options->capture_stdout) {
278		if (pipe(chout) != 0) {
279			openpam_log(PAM_LOG_ERROR, "%s: pipe(): %m", func);
280			OUT(PAM_SYSTEM_ERR);
281		}
282		if (fcntl(chout[0], F_SETFL, O_NONBLOCK) != 0) {
283			openpam_log(PAM_LOG_ERROR, "%s: fcntl(): %m", func);
284			OUT(PAM_SYSTEM_ERR);
285		}
286	} else {
287		if ((chout[1] = open("/dev/null", O_RDWR)) < 0) {
288			openpam_log(PAM_LOG_ERROR, "%s: /dev/null: %m", func);
289			OUT(PAM_SYSTEM_ERR);
290		}
291	}
292	if (options->capture_stderr) {
293		if (pipe(cherr) != 0) {
294			openpam_log(PAM_LOG_ERROR, "%s: pipe(): %m", func);
295			OUT(PAM_SYSTEM_ERR);
296		}
297		if (fcntl(cherr[0], F_SETFL, O_NONBLOCK) != 0) {
298			openpam_log(PAM_LOG_ERROR, "%s: fcntl(): %m", func);
299			OUT(PAM_SYSTEM_ERR);
300		}
301	} else {
302		if ((cherr[1] = open("/dev/null", O_RDWR)) < 0) {
303			openpam_log(PAM_LOG_ERROR, "%s: /dev/null: %m", func);
304			OUT(PAM_SYSTEM_ERR);
305		}
306	}
307
308	if ((pid = pdfork(&pd, 0)) == 0) {
309		/* child */
310		if ((chin[1] >= 0 && close(chin[1]) != 0) ||
311			(chout[0] >= 0 && close(chout[0]) != 0) ||
312		    (cherr[0] >= 0 && close(cherr[0]) != 0)) {
313			openpam_log(PAM_LOG_ERROR, "%s: close(): %m", func);
314		} else if (chin[0] >= 0 &&
315			dup2(chin[0], STDIN_FILENO) != STDIN_FILENO) {
316			openpam_log(PAM_LOG_ERROR, "%s: dup2(): %m", func);
317		} else if (dup2(chout[1], STDOUT_FILENO) != STDOUT_FILENO ||
318		    dup2(cherr[1], STDERR_FILENO) != STDERR_FILENO) {
319			openpam_log(PAM_LOG_ERROR, "%s: dup2(): %m", func);
320		} else {
321			execve(argv[0], (char * const *)argv,
322			    (char * const *)envlist);
323			openpam_log(PAM_LOG_ERROR, "%s: execve(%s): %m",
324			    func, argv[0]);
325		}
326		_exit(1);
327	}
328	/* parent */
329	if (pid == -1) {
330		openpam_log(PAM_LOG_ERROR, "%s: pdfork(): %m", func);
331		OUT(PAM_SYSTEM_ERR);
332	}
333	/* use poll() to watch the process and stdin / stdout / stderr */
334	if (chin[0] >= 0)
335		close(chin[0]);
336	if (chout[1] >= 0)
337		close(chout[1]);
338	if (cherr[1] >= 0)
339		close(cherr[1]);
340	memset(pfd, 0, sizeof pfd);
341	pfd[0].fd = pd;
342	pfd[0].events = POLLHUP;
343	nfds = 1;
344	nreadfds = 0;
345	if (options->capture_stdout) {
346		pfd[nfds].fd = chout[0];
347		pfd[nfds].events = POLLIN|POLLERR|POLLHUP;
348		nfds++;
349		nreadfds++;
350	}
351	if (options->capture_stderr) {
352		pfd[nfds].fd = cherr[0];
353		pfd[nfds].events = POLLIN|POLLERR|POLLHUP;
354		nfds++;
355		nreadfds++;
356	}
357	if (options->expose_authtok) {
358		pfd[nfds].fd = chin[1];
359		pfd[nfds].events = POLLOUT|POLLERR|POLLHUP;
360		nfds++;
361	}
362
363	/* loop until the process exits */
364	do {
365		if (poll(pfd, nfds, INFTIM) < 0) {
366			openpam_log(PAM_LOG_ERROR, "%s: poll(): %m", func);
367			OUT(PAM_SYSTEM_ERR);
368		}
369		/* are the stderr / stdout pipes ready for reading? */
370		for (i = 1; i < 1 + nreadfds; ++i) {
371			if ((pfd[i].revents & POLLIN) == 0)
372				continue;
373			if ((rlen = read(pfd[i].fd, buf, sizeof(buf) - 1)) < 0) {
374				openpam_log(PAM_LOG_ERROR, "%s: read(): %m",
375				    func);
376				OUT(PAM_SYSTEM_ERR);
377			} else if (rlen == 0) {
378				continue;
379			}
380			buf[rlen] = '\0';
381			(void)pam_prompt(pamh, pfd[i].fd == chout[0] ?
382			    PAM_TEXT_INFO : PAM_ERROR_MSG, &resp, "%s", buf);
383		}
384		/* is the stdin pipe ready for writing? */
385		if (options->expose_authtok && authtok_size > 0 &&
386			(pfd[nfds - 1].revents & POLLOUT) != 0) {
387			if ((wlen = write(chin[1], authtok, authtok_size)) < 0) {
388				if (errno == EAGAIN)
389					continue;
390				openpam_log(PAM_LOG_ERROR, "%s: write(): %m",
391				    func);
392				OUT(PAM_SYSTEM_ERR);
393			} else {
394				authtok += wlen;
395				authtok_size -= wlen;
396				if (authtok_size == 0) {
397					/* finished writing; close and forget the pipe */
398					close(chin[1]);
399					chin[1] = -1;
400					nfds--;
401				}
402			}
403		}
404	} while (pfd[0].revents == 0);
405
406	/* the child process has exited */
407	while (waitpid(pid, &status, 0) == -1) {
408		if (errno == EINTR)
409			continue;
410		openpam_log(PAM_LOG_ERROR, "%s: waitpid(): %m", func);
411		OUT(PAM_SYSTEM_ERR);
412	}
413
414	/* check exit code */
415	if (WIFSIGNALED(status)) {
416		openpam_log(PAM_LOG_ERROR, "%s: %s caught signal %d%s",
417		    func, argv[0], WTERMSIG(status),
418		    WCOREDUMP(status) ? " (core dumped)" : "");
419		OUT(PAM_SERVICE_ERR);
420	}
421	if (!WIFEXITED(status)) {
422		openpam_log(PAM_LOG_ERROR, "%s: unknown status 0x%x",
423		    func, status);
424		OUT(PAM_SERVICE_ERR);
425	}
426
427	if (options->return_prog_exit_status) {
428		openpam_log(PAM_LOG_DEBUG,
429		    "%s: Use program exit status as return value: %d",
430		    func, WEXITSTATUS(status));
431		OUT(WEXITSTATUS(status));
432	} else {
433		OUT(WEXITSTATUS(status) == 0 ? PAM_SUCCESS : PAM_PERM_DENIED);
434	}
435	/* unreachable */
436out:
437	serrno = errno;
438	if (pd >= 0)
439		close(pd);
440	if (chin[0] >= 0)
441		close(chin[0]);
442	if (chin[1] >= 0)
443		close(chin[1]);
444	if (chout[0] >= 0)
445		close(chout[0]);
446	if (chout[1] >= 0)
447		close(chout[1]);
448	if (cherr[0] >= 0)
449		close(cherr[0]);
450	if (cherr[0] >= 0)
451		close(cherr[1]);
452	if (envlist != NULL)
453		openpam_free_envlist(envlist);
454	errno = serrno;
455	return (pam_err);
456}
457
458PAM_EXTERN int
459pam_sm_authenticate(pam_handle_t *pamh, int flags,
460    int argc, const char *argv[])
461{
462	int ret;
463	struct pe_opts options;
464
465	ret = parse_options(__func__, &argc, &argv, &options);
466	if (ret != 0)
467		return (PAM_SERVICE_ERR);
468
469	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
470
471	/*
472	 * We must check that the program returned a valid code for this
473	 * function.
474	 */
475	switch (ret) {
476	case PAM_SUCCESS:
477	case PAM_ABORT:
478	case PAM_AUTHINFO_UNAVAIL:
479	case PAM_AUTH_ERR:
480	case PAM_BUF_ERR:
481	case PAM_CONV_ERR:
482	case PAM_CRED_INSUFFICIENT:
483	case PAM_IGNORE:
484	case PAM_MAXTRIES:
485	case PAM_PERM_DENIED:
486	case PAM_SERVICE_ERR:
487	case PAM_SYSTEM_ERR:
488	case PAM_USER_UNKNOWN:
489		break;
490	default:
491		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
492		    argv[0], ret);
493		ret = PAM_SERVICE_ERR;
494	}
495
496	return (ret);
497}
498
499PAM_EXTERN int
500pam_sm_setcred(pam_handle_t *pamh, int flags,
501    int argc, const char *argv[])
502{
503	int ret;
504	struct pe_opts options;
505
506	ret = parse_options(__func__, &argc, &argv, &options);
507	if (ret != 0)
508		return (PAM_SERVICE_ERR);
509
510	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
511
512	/*
513	 * We must check that the program returned a valid code for this
514	 * function.
515	 */
516	switch (ret) {
517	case PAM_SUCCESS:
518	case PAM_ABORT:
519	case PAM_BUF_ERR:
520	case PAM_CONV_ERR:
521	case PAM_CRED_ERR:
522	case PAM_CRED_EXPIRED:
523	case PAM_CRED_UNAVAIL:
524	case PAM_IGNORE:
525	case PAM_PERM_DENIED:
526	case PAM_SERVICE_ERR:
527	case PAM_SYSTEM_ERR:
528	case PAM_USER_UNKNOWN:
529		break;
530	default:
531		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
532		    argv[0], ret);
533		ret = PAM_SERVICE_ERR;
534	}
535
536	return (ret);
537}
538
539PAM_EXTERN int
540pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
541    int argc, const char *argv[])
542{
543	int ret;
544	struct pe_opts options;
545
546	ret = parse_options(__func__, &argc, &argv, &options);
547	if (ret != 0)
548		return (PAM_SERVICE_ERR);
549
550	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
551
552	/*
553	 * We must check that the program returned a valid code for this
554	 * function.
555	 */
556	switch (ret) {
557	case PAM_SUCCESS:
558	case PAM_ABORT:
559	case PAM_ACCT_EXPIRED:
560	case PAM_AUTH_ERR:
561	case PAM_BUF_ERR:
562	case PAM_CONV_ERR:
563	case PAM_IGNORE:
564	case PAM_NEW_AUTHTOK_REQD:
565	case PAM_PERM_DENIED:
566	case PAM_SERVICE_ERR:
567	case PAM_SYSTEM_ERR:
568	case PAM_USER_UNKNOWN:
569		break;
570	default:
571		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
572		    argv[0], ret);
573		ret = PAM_SERVICE_ERR;
574	}
575
576	return (ret);
577}
578
579PAM_EXTERN int
580pam_sm_open_session(pam_handle_t *pamh, int flags,
581    int argc, const char *argv[])
582{
583	int ret;
584	struct pe_opts options;
585
586	ret = parse_options(__func__, &argc, &argv, &options);
587	if (ret != 0)
588		return (PAM_SERVICE_ERR);
589
590	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
591
592	/*
593	 * We must check that the program returned a valid code for this
594	 * function.
595	 */
596	switch (ret) {
597	case PAM_SUCCESS:
598	case PAM_ABORT:
599	case PAM_BUF_ERR:
600	case PAM_CONV_ERR:
601	case PAM_IGNORE:
602	case PAM_PERM_DENIED:
603	case PAM_SERVICE_ERR:
604	case PAM_SESSION_ERR:
605	case PAM_SYSTEM_ERR:
606		break;
607	default:
608		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
609		    argv[0], ret);
610		ret = PAM_SERVICE_ERR;
611	}
612
613	return (ret);
614}
615
616PAM_EXTERN int
617pam_sm_close_session(pam_handle_t *pamh, int flags,
618    int argc, const char *argv[])
619{
620	int ret;
621	struct pe_opts options;
622
623	ret = parse_options(__func__, &argc, &argv, &options);
624	if (ret != 0)
625		return (PAM_SERVICE_ERR);
626
627	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
628
629	/*
630	 * We must check that the program returned a valid code for this
631	 * function.
632	 */
633	switch (ret) {
634	case PAM_SUCCESS:
635	case PAM_ABORT:
636	case PAM_BUF_ERR:
637	case PAM_CONV_ERR:
638	case PAM_IGNORE:
639	case PAM_PERM_DENIED:
640	case PAM_SERVICE_ERR:
641	case PAM_SESSION_ERR:
642	case PAM_SYSTEM_ERR:
643		break;
644	default:
645		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
646		    argv[0], ret);
647		ret = PAM_SERVICE_ERR;
648	}
649
650	return (ret);
651}
652
653PAM_EXTERN int
654pam_sm_chauthtok(pam_handle_t *pamh, int flags,
655    int argc, const char *argv[])
656{
657	int ret;
658	struct pe_opts options;
659
660	ret = parse_options(__func__, &argc, &argv, &options);
661	if (ret != 0)
662		return (PAM_SERVICE_ERR);
663
664	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
665
666	/*
667	 * We must check that the program returned a valid code for this
668	 * function.
669	 */
670	switch (ret) {
671	case PAM_SUCCESS:
672	case PAM_ABORT:
673	case PAM_AUTHTOK_DISABLE_AGING:
674	case PAM_AUTHTOK_ERR:
675	case PAM_AUTHTOK_LOCK_BUSY:
676	case PAM_AUTHTOK_RECOVERY_ERR:
677	case PAM_BUF_ERR:
678	case PAM_CONV_ERR:
679	case PAM_IGNORE:
680	case PAM_PERM_DENIED:
681	case PAM_SERVICE_ERR:
682	case PAM_SYSTEM_ERR:
683	case PAM_TRY_AGAIN:
684		break;
685	default:
686		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
687		    argv[0], ret);
688		ret = PAM_SERVICE_ERR;
689	}
690
691	return (ret);
692}
693
694PAM_MODULE_ENTRY("pam_exec");
695