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 #include <sys/types.h> 41 #include <sys/poll.h> 42 #include <sys/procdesc.h> 43 #include <sys/wait.h> 44 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <unistd.h> 51 52 #include <security/pam_appl.h> 53 #include <security/pam_modules.h> 54 #include <security/openpam.h> 55 56 #define PAM_ITEM_ENV(n) { (n), #n } 57 static struct { 58 int item; 59 const char *name; 60 } pam_item_env[] = { 61 PAM_ITEM_ENV(PAM_SERVICE), 62 PAM_ITEM_ENV(PAM_USER), 63 PAM_ITEM_ENV(PAM_TTY), 64 PAM_ITEM_ENV(PAM_RHOST), 65 PAM_ITEM_ENV(PAM_RUSER), 66 }; 67 #define NUM_PAM_ITEM_ENV (sizeof(pam_item_env) / sizeof(pam_item_env[0])) 68 69 #define PAM_ERR_ENV_X(str, num) str "=" #num 70 #define PAM_ERR_ENV(pam_err) PAM_ERR_ENV_X(#pam_err, pam_err) 71 static const char *pam_err_env[] = { 72 PAM_ERR_ENV(PAM_SUCCESS), 73 PAM_ERR_ENV(PAM_OPEN_ERR), 74 PAM_ERR_ENV(PAM_SYMBOL_ERR), 75 PAM_ERR_ENV(PAM_SERVICE_ERR), 76 PAM_ERR_ENV(PAM_SYSTEM_ERR), 77 PAM_ERR_ENV(PAM_BUF_ERR), 78 PAM_ERR_ENV(PAM_CONV_ERR), 79 PAM_ERR_ENV(PAM_PERM_DENIED), 80 PAM_ERR_ENV(PAM_MAXTRIES), 81 PAM_ERR_ENV(PAM_AUTH_ERR), 82 PAM_ERR_ENV(PAM_NEW_AUTHTOK_REQD), 83 PAM_ERR_ENV(PAM_CRED_INSUFFICIENT), 84 PAM_ERR_ENV(PAM_AUTHINFO_UNAVAIL), 85 PAM_ERR_ENV(PAM_USER_UNKNOWN), 86 PAM_ERR_ENV(PAM_CRED_UNAVAIL), 87 PAM_ERR_ENV(PAM_CRED_EXPIRED), 88 PAM_ERR_ENV(PAM_CRED_ERR), 89 PAM_ERR_ENV(PAM_ACCT_EXPIRED), 90 PAM_ERR_ENV(PAM_AUTHTOK_EXPIRED), 91 PAM_ERR_ENV(PAM_SESSION_ERR), 92 PAM_ERR_ENV(PAM_AUTHTOK_ERR), 93 PAM_ERR_ENV(PAM_AUTHTOK_RECOVERY_ERR), 94 PAM_ERR_ENV(PAM_AUTHTOK_LOCK_BUSY), 95 PAM_ERR_ENV(PAM_AUTHTOK_DISABLE_AGING), 96 PAM_ERR_ENV(PAM_NO_MODULE_DATA), 97 PAM_ERR_ENV(PAM_IGNORE), 98 PAM_ERR_ENV(PAM_ABORT), 99 PAM_ERR_ENV(PAM_TRY_AGAIN), 100 PAM_ERR_ENV(PAM_MODULE_UNKNOWN), 101 PAM_ERR_ENV(PAM_DOMAIN_UNKNOWN), 102 PAM_ERR_ENV(PAM_NUM_ERR), 103 }; 104 #define NUM_PAM_ERR_ENV (sizeof(pam_err_env) / sizeof(pam_err_env[0])) 105 106 struct pe_opts { 107 int return_prog_exit_status; 108 int capture_stdout; 109 int capture_stderr; 110 int expose_authtok; 111 int use_first_pass; 112 }; 113 114 static int 115 parse_options(const char *func, int *argc, const char **argv[], 116 struct pe_opts *options) 117 { 118 int i; 119 120 /* 121 * Parse options: 122 * return_prog_exit_status: 123 * use the program exit status as the return code of pam_exec 124 * --: 125 * stop options parsing; what follows is the command to execute 126 */ 127 memset(options, 0, sizeof(*options)); 128 129 for (i = 0; i < *argc; ++i) { 130 if (strcmp((*argv)[i], "debug") == 0 || 131 strcmp((*argv)[i], "no_warn") == 0) { 132 /* ignore */ 133 } else if (strcmp((*argv)[i], "capture_stdout") == 0) { 134 options->capture_stdout = 1; 135 } else if (strcmp((*argv)[i], "capture_stderr") == 0) { 136 options->capture_stderr = 1; 137 } else if (strcmp((*argv)[i], "return_prog_exit_status") == 0) { 138 options->return_prog_exit_status = 1; 139 } else if (strcmp((*argv)[i], "expose_authtok") == 0) { 140 options->expose_authtok = 1; 141 } else if (strcmp((*argv)[i], "use_first_pass") == 0) { 142 options->use_first_pass = 1; 143 } else { 144 if (strcmp((*argv)[i], "--") == 0) { 145 (*argc)--; 146 (*argv)++; 147 } 148 break; 149 } 150 openpam_log(PAM_LOG_DEBUG, "%s: option \"%s\" enabled", 151 func, (*argv)[i]); 152 } 153 154 (*argc) -= i; 155 (*argv) += i; 156 157 return (0); 158 } 159 160 static int 161 _pam_exec(pam_handle_t *pamh, 162 const char *func, int flags __unused, int argc, const char *argv[], 163 struct pe_opts *options) 164 { 165 char buf[PAM_MAX_MSG_SIZE]; 166 struct pollfd pfd[4]; 167 const void *item; 168 char **envlist, *envstr, *resp, **tmp; 169 ssize_t rlen, wlen; 170 int envlen, extralen, i; 171 int pam_err, serrno, status; 172 int chin[2], chout[2], cherr[2], pd; 173 nfds_t nfds, nreadfds; 174 pid_t pid; 175 const char *authtok; 176 size_t authtok_size; 177 int rc; 178 179 pd = -1; 180 pid = 0; 181 chin[0] = chin[1] = chout[0] = chout[1] = cherr[0] = cherr[1] = -1; 182 envlist = NULL; 183 184 #define OUT(ret) do { pam_err = (ret); goto out; } while (0) 185 186 /* Check there's a program name left after parsing options. */ 187 if (argc < 1) { 188 openpam_log(PAM_LOG_ERROR, "%s: No program specified: aborting", 189 func); 190 OUT(PAM_SERVICE_ERR); 191 } 192 193 /* 194 * Set up the child's environment list. It consists of the PAM 195 * environment, a few hand-picked PAM items, the name of the 196 * service function, and if return_prog_exit_status is set, the 197 * numerical values of all PAM error codes. 198 */ 199 200 /* compute the final size of the environment. */ 201 envlist = pam_getenvlist(pamh); 202 for (envlen = 0; envlist[envlen] != NULL; ++envlen) 203 /* nothing */ ; 204 extralen = NUM_PAM_ITEM_ENV + 1; 205 if (options->return_prog_exit_status) 206 extralen += NUM_PAM_ERR_ENV; 207 tmp = reallocarray(envlist, envlen + extralen + 1, sizeof(*envlist)); 208 openpam_log(PAM_LOG_DEBUG, "envlen = %d extralen = %d tmp = %p", 209 envlen, extralen, tmp); 210 if (tmp == NULL) 211 OUT(PAM_BUF_ERR); 212 envlist = tmp; 213 extralen += envlen; 214 215 /* copy selected PAM items to the environment */ 216 for (i = 0; i < NUM_PAM_ITEM_ENV; ++i) { 217 pam_err = pam_get_item(pamh, pam_item_env[i].item, &item); 218 if (pam_err != PAM_SUCCESS || item == NULL) 219 continue; 220 if (asprintf(&envstr, "%s=%s", pam_item_env[i].name, 221 (const char *)item) < 0) 222 OUT(PAM_BUF_ERR); 223 envlist[envlen++] = envstr; 224 envlist[envlen] = NULL; 225 openpam_log(PAM_LOG_DEBUG, "setenv %s", envstr); 226 } 227 228 /* add the name of the service function to the environment */ 229 if (asprintf(&envstr, "PAM_SM_FUNC=%s", func) < 0) 230 OUT(PAM_BUF_ERR); 231 envlist[envlen++] = envstr; 232 envlist[envlen] = NULL; 233 234 /* add the PAM error codes to the environment. */ 235 if (options->return_prog_exit_status) { 236 for (i = 0; i < (int)NUM_PAM_ERR_ENV; ++i) { 237 if ((envstr = strdup(pam_err_env[i])) == NULL) 238 OUT(PAM_BUF_ERR); 239 envlist[envlen++] = envstr; 240 envlist[envlen] = NULL; 241 } 242 } 243 244 openpam_log(PAM_LOG_DEBUG, "envlen = %d extralen = %d envlist = %p", 245 envlen, extralen, envlist); 246 247 /* set up pipe and get authtok if requested */ 248 if (options->expose_authtok) { 249 if (pipe(chin) != 0) { 250 openpam_log(PAM_LOG_ERROR, "%s: pipe(): %m", func); 251 OUT(PAM_SYSTEM_ERR); 252 } 253 if (fcntl(chin[1], F_SETFL, O_NONBLOCK)) { 254 openpam_log(PAM_LOG_ERROR, "%s: fcntl(): %m", func); 255 OUT(PAM_SYSTEM_ERR); 256 } 257 if (options->use_first_pass || 258 strcmp(func, "pam_sm_setcred") == 0) { 259 /* don't prompt, only expose existing token */ 260 rc = pam_get_item(pamh, PAM_AUTHTOK, &item); 261 authtok = item; 262 if (authtok == NULL && rc == PAM_SUCCESS) { 263 openpam_log(PAM_LOG_ERROR, 264 "%s: pam_get_authtok(): %s", 265 func, "authentication token not available"); 266 OUT(PAM_SYSTEM_ERR); 267 } 268 269 } else { 270 rc = pam_get_authtok(pamh, PAM_AUTHTOK, &authtok, NULL); 271 } 272 if (rc == PAM_SUCCESS) { 273 /* We include the trailing null terminator. */ 274 authtok_size = strlen(authtok) + 1; 275 } else { 276 openpam_log(PAM_LOG_ERROR, "%s: pam_get_authtok(): %s", 277 func, pam_strerror(pamh, rc)); 278 OUT(PAM_SYSTEM_ERR); 279 } 280 } 281 /* set up pipes if capture was requested */ 282 if (options->capture_stdout) { 283 if (pipe(chout) != 0) { 284 openpam_log(PAM_LOG_ERROR, "%s: pipe(): %m", func); 285 OUT(PAM_SYSTEM_ERR); 286 } 287 if (fcntl(chout[0], F_SETFL, O_NONBLOCK) != 0) { 288 openpam_log(PAM_LOG_ERROR, "%s: fcntl(): %m", func); 289 OUT(PAM_SYSTEM_ERR); 290 } 291 } else { 292 if ((chout[1] = open("/dev/null", O_RDWR)) < 0) { 293 openpam_log(PAM_LOG_ERROR, "%s: /dev/null: %m", func); 294 OUT(PAM_SYSTEM_ERR); 295 } 296 } 297 if (options->capture_stderr) { 298 if (pipe(cherr) != 0) { 299 openpam_log(PAM_LOG_ERROR, "%s: pipe(): %m", func); 300 OUT(PAM_SYSTEM_ERR); 301 } 302 if (fcntl(cherr[0], F_SETFL, O_NONBLOCK) != 0) { 303 openpam_log(PAM_LOG_ERROR, "%s: fcntl(): %m", func); 304 OUT(PAM_SYSTEM_ERR); 305 } 306 } else { 307 if ((cherr[1] = open("/dev/null", O_RDWR)) < 0) { 308 openpam_log(PAM_LOG_ERROR, "%s: /dev/null: %m", func); 309 OUT(PAM_SYSTEM_ERR); 310 } 311 } 312 313 if ((pid = pdfork(&pd, 0)) == 0) { 314 /* child */ 315 if ((chin[1] >= 0 && close(chin[1]) != 0) || 316 (chout[0] >= 0 && close(chout[0]) != 0) || 317 (cherr[0] >= 0 && close(cherr[0]) != 0)) { 318 openpam_log(PAM_LOG_ERROR, "%s: close(): %m", func); 319 } else if (chin[0] >= 0 && 320 dup2(chin[0], STDIN_FILENO) != STDIN_FILENO) { 321 openpam_log(PAM_LOG_ERROR, "%s: dup2(): %m", func); 322 } else if (dup2(chout[1], STDOUT_FILENO) != STDOUT_FILENO || 323 dup2(cherr[1], STDERR_FILENO) != STDERR_FILENO) { 324 openpam_log(PAM_LOG_ERROR, "%s: dup2(): %m", func); 325 } else { 326 execve(argv[0], (char * const *)argv, 327 (char * const *)envlist); 328 openpam_log(PAM_LOG_ERROR, "%s: execve(%s): %m", 329 func, argv[0]); 330 } 331 _exit(1); 332 } 333 /* parent */ 334 if (pid == -1) { 335 openpam_log(PAM_LOG_ERROR, "%s: pdfork(): %m", func); 336 OUT(PAM_SYSTEM_ERR); 337 } 338 /* use poll() to watch the process and stdin / stdout / stderr */ 339 if (chin[0] >= 0) 340 close(chin[0]); 341 if (chout[1] >= 0) 342 close(chout[1]); 343 if (cherr[1] >= 0) 344 close(cherr[1]); 345 memset(pfd, 0, sizeof pfd); 346 pfd[0].fd = pd; 347 pfd[0].events = POLLHUP; 348 nfds = 1; 349 nreadfds = 0; 350 if (options->capture_stdout) { 351 pfd[nfds].fd = chout[0]; 352 pfd[nfds].events = POLLIN|POLLERR|POLLHUP; 353 nfds++; 354 nreadfds++; 355 } 356 if (options->capture_stderr) { 357 pfd[nfds].fd = cherr[0]; 358 pfd[nfds].events = POLLIN|POLLERR|POLLHUP; 359 nfds++; 360 nreadfds++; 361 } 362 if (options->expose_authtok) { 363 pfd[nfds].fd = chin[1]; 364 pfd[nfds].events = POLLOUT|POLLERR|POLLHUP; 365 nfds++; 366 } 367 368 /* loop until the process exits */ 369 do { 370 if (poll(pfd, nfds, INFTIM) < 0) { 371 openpam_log(PAM_LOG_ERROR, "%s: poll(): %m", func); 372 OUT(PAM_SYSTEM_ERR); 373 } 374 /* are the stderr / stdout pipes ready for reading? */ 375 for (i = 1; i < 1 + nreadfds; ++i) { 376 if ((pfd[i].revents & POLLIN) == 0) 377 continue; 378 if ((rlen = read(pfd[i].fd, buf, sizeof(buf) - 1)) < 0) { 379 openpam_log(PAM_LOG_ERROR, "%s: read(): %m", 380 func); 381 OUT(PAM_SYSTEM_ERR); 382 } else if (rlen == 0) { 383 continue; 384 } 385 buf[rlen] = '\0'; 386 (void)pam_prompt(pamh, pfd[i].fd == chout[0] ? 387 PAM_TEXT_INFO : PAM_ERROR_MSG, &resp, "%s", buf); 388 } 389 /* is the stdin pipe ready for writing? */ 390 if (options->expose_authtok && authtok_size > 0 && 391 (pfd[nfds - 1].revents & POLLOUT) != 0) { 392 if ((wlen = write(chin[1], authtok, authtok_size)) < 0) { 393 if (errno == EAGAIN) 394 continue; 395 openpam_log(PAM_LOG_ERROR, "%s: write(): %m", 396 func); 397 OUT(PAM_SYSTEM_ERR); 398 } else { 399 authtok += wlen; 400 authtok_size -= wlen; 401 if (authtok_size == 0) { 402 /* finished writing; close and forget the pipe */ 403 close(chin[1]); 404 chin[1] = -1; 405 nfds--; 406 } 407 } 408 } 409 } while (pfd[0].revents == 0); 410 411 /* the child process has exited */ 412 while (waitpid(pid, &status, 0) == -1) { 413 if (errno == EINTR) 414 continue; 415 openpam_log(PAM_LOG_ERROR, "%s: waitpid(): %m", func); 416 OUT(PAM_SYSTEM_ERR); 417 } 418 419 /* check exit code */ 420 if (WIFSIGNALED(status)) { 421 openpam_log(PAM_LOG_ERROR, "%s: %s caught signal %d%s", 422 func, argv[0], WTERMSIG(status), 423 WCOREDUMP(status) ? " (core dumped)" : ""); 424 OUT(PAM_SERVICE_ERR); 425 } 426 if (!WIFEXITED(status)) { 427 openpam_log(PAM_LOG_ERROR, "%s: unknown status 0x%x", 428 func, status); 429 OUT(PAM_SERVICE_ERR); 430 } 431 432 if (options->return_prog_exit_status) { 433 openpam_log(PAM_LOG_DEBUG, 434 "%s: Use program exit status as return value: %d", 435 func, WEXITSTATUS(status)); 436 OUT(WEXITSTATUS(status)); 437 } else { 438 OUT(WEXITSTATUS(status) == 0 ? PAM_SUCCESS : PAM_PERM_DENIED); 439 } 440 /* unreachable */ 441 out: 442 serrno = errno; 443 if (pd >= 0) 444 close(pd); 445 if (chin[0] >= 0) 446 close(chin[0]); 447 if (chin[1] >= 0) 448 close(chin[1]); 449 if (chout[0] >= 0) 450 close(chout[0]); 451 if (chout[1] >= 0) 452 close(chout[1]); 453 if (cherr[0] >= 0) 454 close(cherr[0]); 455 if (cherr[0] >= 0) 456 close(cherr[1]); 457 if (envlist != NULL) 458 openpam_free_envlist(envlist); 459 errno = serrno; 460 return (pam_err); 461 } 462 463 PAM_EXTERN int 464 pam_sm_authenticate(pam_handle_t *pamh, int flags, 465 int argc, const char *argv[]) 466 { 467 int ret; 468 struct pe_opts options; 469 470 ret = parse_options(__func__, &argc, &argv, &options); 471 if (ret != 0) 472 return (PAM_SERVICE_ERR); 473 474 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options); 475 476 /* 477 * We must check that the program returned a valid code for this 478 * function. 479 */ 480 switch (ret) { 481 case PAM_SUCCESS: 482 case PAM_ABORT: 483 case PAM_AUTHINFO_UNAVAIL: 484 case PAM_AUTH_ERR: 485 case PAM_BUF_ERR: 486 case PAM_CONV_ERR: 487 case PAM_CRED_INSUFFICIENT: 488 case PAM_IGNORE: 489 case PAM_MAXTRIES: 490 case PAM_PERM_DENIED: 491 case PAM_SERVICE_ERR: 492 case PAM_SYSTEM_ERR: 493 case PAM_USER_UNKNOWN: 494 break; 495 default: 496 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d", 497 argv[0], ret); 498 ret = PAM_SERVICE_ERR; 499 } 500 501 return (ret); 502 } 503 504 PAM_EXTERN int 505 pam_sm_setcred(pam_handle_t *pamh, int flags, 506 int argc, const char *argv[]) 507 { 508 int ret; 509 struct pe_opts options; 510 511 ret = parse_options(__func__, &argc, &argv, &options); 512 if (ret != 0) 513 return (PAM_SERVICE_ERR); 514 515 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options); 516 517 /* 518 * We must check that the program returned a valid code for this 519 * function. 520 */ 521 switch (ret) { 522 case PAM_SUCCESS: 523 case PAM_ABORT: 524 case PAM_BUF_ERR: 525 case PAM_CONV_ERR: 526 case PAM_CRED_ERR: 527 case PAM_CRED_EXPIRED: 528 case PAM_CRED_UNAVAIL: 529 case PAM_IGNORE: 530 case PAM_PERM_DENIED: 531 case PAM_SERVICE_ERR: 532 case PAM_SYSTEM_ERR: 533 case PAM_USER_UNKNOWN: 534 break; 535 default: 536 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d", 537 argv[0], ret); 538 ret = PAM_SERVICE_ERR; 539 } 540 541 return (ret); 542 } 543 544 PAM_EXTERN int 545 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, 546 int argc, const char *argv[]) 547 { 548 int ret; 549 struct pe_opts options; 550 551 ret = parse_options(__func__, &argc, &argv, &options); 552 if (ret != 0) 553 return (PAM_SERVICE_ERR); 554 555 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options); 556 557 /* 558 * We must check that the program returned a valid code for this 559 * function. 560 */ 561 switch (ret) { 562 case PAM_SUCCESS: 563 case PAM_ABORT: 564 case PAM_ACCT_EXPIRED: 565 case PAM_AUTH_ERR: 566 case PAM_BUF_ERR: 567 case PAM_CONV_ERR: 568 case PAM_IGNORE: 569 case PAM_NEW_AUTHTOK_REQD: 570 case PAM_PERM_DENIED: 571 case PAM_SERVICE_ERR: 572 case PAM_SYSTEM_ERR: 573 case PAM_USER_UNKNOWN: 574 break; 575 default: 576 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d", 577 argv[0], ret); 578 ret = PAM_SERVICE_ERR; 579 } 580 581 return (ret); 582 } 583 584 PAM_EXTERN int 585 pam_sm_open_session(pam_handle_t *pamh, int flags, 586 int argc, const char *argv[]) 587 { 588 int ret; 589 struct pe_opts options; 590 591 ret = parse_options(__func__, &argc, &argv, &options); 592 if (ret != 0) 593 return (PAM_SERVICE_ERR); 594 595 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options); 596 597 /* 598 * We must check that the program returned a valid code for this 599 * function. 600 */ 601 switch (ret) { 602 case PAM_SUCCESS: 603 case PAM_ABORT: 604 case PAM_BUF_ERR: 605 case PAM_CONV_ERR: 606 case PAM_IGNORE: 607 case PAM_PERM_DENIED: 608 case PAM_SERVICE_ERR: 609 case PAM_SESSION_ERR: 610 case PAM_SYSTEM_ERR: 611 break; 612 default: 613 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d", 614 argv[0], ret); 615 ret = PAM_SERVICE_ERR; 616 } 617 618 return (ret); 619 } 620 621 PAM_EXTERN int 622 pam_sm_close_session(pam_handle_t *pamh, int flags, 623 int argc, const char *argv[]) 624 { 625 int ret; 626 struct pe_opts options; 627 628 ret = parse_options(__func__, &argc, &argv, &options); 629 if (ret != 0) 630 return (PAM_SERVICE_ERR); 631 632 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options); 633 634 /* 635 * We must check that the program returned a valid code for this 636 * function. 637 */ 638 switch (ret) { 639 case PAM_SUCCESS: 640 case PAM_ABORT: 641 case PAM_BUF_ERR: 642 case PAM_CONV_ERR: 643 case PAM_IGNORE: 644 case PAM_PERM_DENIED: 645 case PAM_SERVICE_ERR: 646 case PAM_SESSION_ERR: 647 case PAM_SYSTEM_ERR: 648 break; 649 default: 650 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d", 651 argv[0], ret); 652 ret = PAM_SERVICE_ERR; 653 } 654 655 return (ret); 656 } 657 658 PAM_EXTERN int 659 pam_sm_chauthtok(pam_handle_t *pamh, int flags, 660 int argc, const char *argv[]) 661 { 662 int ret; 663 struct pe_opts options; 664 665 ret = parse_options(__func__, &argc, &argv, &options); 666 if (ret != 0) 667 return (PAM_SERVICE_ERR); 668 669 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options); 670 671 /* 672 * We must check that the program returned a valid code for this 673 * function. 674 */ 675 switch (ret) { 676 case PAM_SUCCESS: 677 case PAM_ABORT: 678 case PAM_AUTHTOK_DISABLE_AGING: 679 case PAM_AUTHTOK_ERR: 680 case PAM_AUTHTOK_LOCK_BUSY: 681 case PAM_AUTHTOK_RECOVERY_ERR: 682 case PAM_BUF_ERR: 683 case PAM_CONV_ERR: 684 case PAM_IGNORE: 685 case PAM_PERM_DENIED: 686 case PAM_SERVICE_ERR: 687 case PAM_SYSTEM_ERR: 688 case PAM_TRY_AGAIN: 689 break; 690 default: 691 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d", 692 argv[0], ret); 693 ret = PAM_SERVICE_ERR; 694 } 695 696 return (ret); 697 } 698 699 PAM_MODULE_ENTRY("pam_exec"); 700