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