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"); 702