]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libpam/modules/pam_exec/pam_exec.c
openssh: cherry-pick OpenSSL 1.1.1 compatibility
[FreeBSD/FreeBSD.git] / lib / libpam / modules / pam_exec / pam_exec.c
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");