xref: /freebsd/lib/libpam/modules/pam_exec/pam_exec.c (revision 36712a94975f5bd0d26c85377283b49a2369c82f)
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 }
59 static 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)
73 static 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 
108 struct 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 
116 static int
117 parse_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 
162 static 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 */
435 out:
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 
457 PAM_EXTERN int
458 pam_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 
498 PAM_EXTERN int
499 pam_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 
538 PAM_EXTERN int
539 pam_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 
578 PAM_EXTERN int
579 pam_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 
615 PAM_EXTERN int
616 pam_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 
652 PAM_EXTERN int
653 pam_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 
693 PAM_MODULE_ENTRY("pam_exec");
694