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