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