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