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