xref: /freebsd/lib/libpam/modules/pam_exec/pam_exec.c (revision e0c4386e)
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 }
56 static 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)
70 static 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 
105 struct 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 
113 static int
114 parse_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 
159 static 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 */
440 out:
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 
462 PAM_EXTERN int
463 pam_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 
503 PAM_EXTERN int
504 pam_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 
543 PAM_EXTERN int
544 pam_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 
583 PAM_EXTERN int
584 pam_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 
620 PAM_EXTERN int
621 pam_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 
657 PAM_EXTERN int
658 pam_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 
698 PAM_MODULE_ENTRY("pam_exec");
699