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