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
parse_options(const char * func,int * argc,const char ** argv[],struct pe_opts * options)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
_pam_exec(pam_handle_t * pamh,const char * func,int flags __unused,int argc,const char * argv[],struct pe_opts * options)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
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char * argv[])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
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char * argv[])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
pam_sm_acct_mgmt(pam_handle_t * pamh,int flags,int argc,const char * argv[])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
pam_sm_open_session(pam_handle_t * pamh,int flags,int argc,const char * argv[])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
pam_sm_close_session(pam_handle_t * pamh,int flags,int argc,const char * argv[])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
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char * argv[])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