1 /* 2 * Copyright (c) 2000 Damien Miller. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25 #include "includes.h" 26 27 #ifdef USE_PAM 28 #include <security/pam_appl.h> 29 #include "ssh.h" 30 #include "xmalloc.h" 31 #include "log.h" 32 #include "servconf.h" 33 #include "readpass.h" 34 #include "canohost.h" 35 36 RCSID("$FreeBSD$"); 37 38 #define NEW_AUTHTOK_MSG \ 39 "Warning: Your password has expired, please change it now" 40 41 #define SSHD_PAM_SERVICE "sshd" 42 #define PAM_STRERROR(a, b) pam_strerror((a), (b)) 43 44 /* Callbacks */ 45 static int do_pam_conversation(int num_msg, const struct pam_message **msg, 46 struct pam_response **resp, void *appdata_ptr); 47 void do_pam_cleanup_proc(void *context); 48 void pam_msg_cat(const char *msg); 49 50 /* module-local variables */ 51 static struct pam_conv conv = { 52 do_pam_conversation, 53 NULL 54 }; 55 static pam_handle_t *pamh = NULL; 56 static const char *pampasswd = NULL; 57 static char *pam_msg = NULL; 58 extern ServerOptions options; 59 60 /* states for do_pam_conversation() */ 61 typedef enum { INITIAL_LOGIN, OTHER } pamstates; 62 static pamstates pamstate = INITIAL_LOGIN; 63 /* remember whether pam_acct_mgmt() returned PAM_NEWAUTHTOK_REQD */ 64 static int password_change_required = 0; 65 /* remember whether the last pam_authenticate() succeeded or not */ 66 static int was_authenticated = 0; 67 68 /* Remember what has been initialised */ 69 static int session_opened = 0; 70 static int creds_set = 0; 71 72 /* 73 * accessor which allows us to switch conversation structs according to 74 * the authentication method being used 75 */ 76 void do_pam_set_conv(struct pam_conv *conv) 77 { 78 pam_set_item(pamh, PAM_CONV, conv); 79 } 80 81 /* 82 * PAM conversation function. 83 * There are two states this can run in. 84 * 85 * INITIAL_LOGIN mode simply feeds the password from the client into 86 * PAM in response to PAM_PROMPT_ECHO_OFF, and collects output 87 * messages with pam_msg_cat(). This is used during initial 88 * authentication to bypass the normal PAM password prompt. 89 * 90 * OTHER mode handles PAM_PROMPT_ECHO_OFF with read_passphrase(prompt, 1) 91 * and outputs messages to stderr. This mode is used if pam_chauthtok() 92 * is called to update expired passwords. 93 */ 94 static int do_pam_conversation(int num_msg, const struct pam_message **msg, 95 struct pam_response **resp, void *appdata_ptr) 96 { 97 struct pam_response *reply; 98 int count; 99 char buf[1024]; 100 101 /* PAM will free this later */ 102 reply = malloc(num_msg * sizeof(*reply)); 103 if (reply == NULL) 104 return PAM_CONV_ERR; 105 106 for (count = 0; count < num_msg; count++) { 107 switch ((*msg)[count].msg_style) { 108 case PAM_PROMPT_ECHO_ON: 109 if (pamstate == INITIAL_LOGIN) { 110 free(reply); 111 return PAM_CONV_ERR; 112 } else { 113 fputs((*msg)[count].msg, stderr); 114 fgets(buf, sizeof(buf), stdin); 115 reply[count].resp = xstrdup(buf); 116 reply[count].resp_retcode = PAM_SUCCESS; 117 break; 118 } 119 case PAM_PROMPT_ECHO_OFF: 120 if (pamstate == INITIAL_LOGIN) { 121 if (pampasswd == NULL) { 122 free(reply); 123 return PAM_CONV_ERR; 124 } 125 reply[count].resp = xstrdup(pampasswd); 126 } else { 127 reply[count].resp = 128 xstrdup(read_passphrase((*msg)[count].msg, 1)); 129 } 130 reply[count].resp_retcode = PAM_SUCCESS; 131 break; 132 case PAM_ERROR_MSG: 133 case PAM_TEXT_INFO: 134 if ((*msg)[count].msg != NULL) { 135 if (pamstate == INITIAL_LOGIN) 136 pam_msg_cat((*msg)[count].msg); 137 else { 138 fputs((*msg)[count].msg, stderr); 139 fputs("\n", stderr); 140 } 141 } 142 reply[count].resp = xstrdup(""); 143 reply[count].resp_retcode = PAM_SUCCESS; 144 break; 145 default: 146 free(reply); 147 return PAM_CONV_ERR; 148 } 149 } 150 151 *resp = reply; 152 153 return PAM_SUCCESS; 154 } 155 156 /* Called at exit to cleanly shutdown PAM */ 157 void do_pam_cleanup_proc(void *context) 158 { 159 int pam_retval; 160 161 if (pamh != NULL && session_opened) { 162 pam_retval = pam_close_session(pamh, 0); 163 if (pam_retval != PAM_SUCCESS) { 164 log("Cannot close PAM session[%d]: %.200s", 165 pam_retval, PAM_STRERROR(pamh, pam_retval)); 166 } 167 } 168 169 if (pamh != NULL && creds_set) { 170 pam_retval = pam_setcred(pamh, PAM_DELETE_CRED); 171 if (pam_retval != PAM_SUCCESS) { 172 debug("Cannot delete credentials[%d]: %.200s", 173 pam_retval, PAM_STRERROR(pamh, pam_retval)); 174 } 175 } 176 177 if (pamh != NULL) { 178 pam_retval = pam_end(pamh, pam_retval); 179 if (pam_retval != PAM_SUCCESS) { 180 log("Cannot release PAM authentication[%d]: %.200s", 181 pam_retval, PAM_STRERROR(pamh, pam_retval)); 182 } 183 } 184 } 185 186 /* Attempt password authentation using PAM */ 187 int auth_pam_password(Authctxt *authctxt, const char *password) 188 { 189 struct passwd *pw = authctxt->pw; 190 int pam_retval; 191 192 do_pam_set_conv(&conv); 193 194 /* deny if no user. */ 195 if (pw == NULL) 196 return 0; 197 if (pw->pw_uid == 0 && options.permit_root_login == 2) 198 return 0; 199 if (*password == '\0' && options.permit_empty_passwd == 0) 200 return 0; 201 202 pampasswd = password; 203 204 pamstate = INITIAL_LOGIN; 205 pam_retval = pam_authenticate(pamh, 0); 206 was_authenticated = (pam_retval == PAM_SUCCESS); 207 if (pam_retval == PAM_SUCCESS) { 208 debug("PAM Password authentication accepted for user \"%.100s\"", 209 pw->pw_name); 210 return 1; 211 } else { 212 debug("PAM Password authentication for \"%.100s\" failed[%d]: %s", 213 pw->pw_name, pam_retval, PAM_STRERROR(pamh, pam_retval)); 214 return 0; 215 } 216 } 217 218 /* Do account management using PAM */ 219 int do_pam_account(char *username, char *remote_user) 220 { 221 int pam_retval; 222 223 do_pam_set_conv(&conv); 224 225 debug("PAM setting rhost to \"%.200s\"", 226 get_canonical_hostname(options.reverse_mapping_check)); 227 pam_retval = pam_set_item(pamh, PAM_RHOST, 228 get_canonical_hostname(options.reverse_mapping_check)); 229 if (pam_retval != PAM_SUCCESS) { 230 fatal("PAM set rhost failed[%d]: %.200s", 231 pam_retval, PAM_STRERROR(pamh, pam_retval)); 232 } 233 234 if (remote_user != NULL) { 235 debug("PAM setting ruser to \"%.200s\"", remote_user); 236 pam_retval = pam_set_item(pamh, PAM_RUSER, remote_user); 237 if (pam_retval != PAM_SUCCESS) { 238 fatal("PAM set ruser failed[%d]: %.200s", 239 pam_retval, PAM_STRERROR(pamh, pam_retval)); 240 } 241 } 242 243 pam_retval = pam_acct_mgmt(pamh, 0); 244 switch (pam_retval) { 245 case PAM_SUCCESS: 246 /* This is what we want */ 247 break; 248 case PAM_NEW_AUTHTOK_REQD: 249 pam_msg_cat(NEW_AUTHTOK_MSG); 250 /* flag that password change is necessary */ 251 password_change_required = 1; 252 break; 253 default: 254 log("PAM rejected by account configuration[%d]: %.200s", 255 pam_retval, PAM_STRERROR(pamh, pam_retval)); 256 return(0); 257 } 258 259 return(1); 260 } 261 262 /* Do PAM-specific session initialisation */ 263 void do_pam_session(char *username, const char *ttyname) 264 { 265 int pam_retval; 266 267 do_pam_set_conv(&conv); 268 269 if (ttyname != NULL) { 270 debug("PAM setting tty to \"%.200s\"", ttyname); 271 pam_retval = pam_set_item(pamh, PAM_TTY, ttyname); 272 if (pam_retval != PAM_SUCCESS) { 273 fatal("PAM set tty failed[%d]: %.200s", 274 pam_retval, PAM_STRERROR(pamh, pam_retval)); 275 } 276 } 277 278 debug("do_pam_session: euid %u, uid %u", geteuid(), getuid()); 279 pam_retval = pam_open_session(pamh, 0); 280 if (pam_retval != PAM_SUCCESS) { 281 fatal("PAM session setup failed[%d]: %.200s", 282 pam_retval, PAM_STRERROR(pamh, pam_retval)); 283 } 284 285 session_opened = 1; 286 } 287 288 /* Set PAM credentials */ 289 void do_pam_setcred(void) 290 { 291 int pam_retval; 292 293 do_pam_set_conv(&conv); 294 295 debug("PAM establishing creds"); 296 pam_retval = pam_setcred(pamh, PAM_ESTABLISH_CRED); 297 if (pam_retval != PAM_SUCCESS) { 298 if (was_authenticated) 299 fatal("PAM setcred failed[%d]: %.200s", 300 pam_retval, PAM_STRERROR(pamh, pam_retval)); 301 else 302 debug("PAM setcred failed[%d]: %.200s", 303 pam_retval, PAM_STRERROR(pamh, pam_retval)); 304 } else 305 creds_set = 1; 306 } 307 308 /* accessor function for file scope static variable */ 309 int pam_password_change_required(void) 310 { 311 return password_change_required; 312 } 313 314 /* 315 * Have user change authentication token if pam_acct_mgmt() indicated 316 * it was expired. This needs to be called after an interactive 317 * session is established and the user's pty is connected to 318 * stdin/stout/stderr. 319 */ 320 void do_pam_chauthtok(void) 321 { 322 int pam_retval; 323 324 do_pam_set_conv(&conv); 325 326 if (password_change_required) { 327 pamstate = OTHER; 328 /* 329 * XXX: should we really loop forever? 330 */ 331 do { 332 pam_retval = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); 333 if (pam_retval != PAM_SUCCESS) { 334 log("PAM pam_chauthtok failed[%d]: %.200s", 335 pam_retval, PAM_STRERROR(pamh, pam_retval)); 336 } 337 } while (pam_retval != PAM_SUCCESS); 338 } 339 } 340 341 /* Cleanly shutdown PAM */ 342 void finish_pam(void) 343 { 344 do_pam_cleanup_proc(NULL); 345 fatal_remove_cleanup(&do_pam_cleanup_proc, NULL); 346 } 347 348 /* Start PAM authentication for specified account */ 349 void start_pam(struct passwd *pw) 350 { 351 int pam_retval; 352 353 debug("Starting up PAM with username \"%.200s\"", pw->pw_name); 354 355 pam_retval = pam_start(SSHD_PAM_SERVICE, pw->pw_name, &conv, &pamh); 356 357 if (pam_retval != PAM_SUCCESS) { 358 fatal("PAM initialisation failed[%d]: %.200s", 359 pam_retval, PAM_STRERROR(pamh, pam_retval)); 360 } 361 362 #ifdef PAM_TTY_KLUDGE 363 /* 364 * Some PAM modules (e.g. pam_time) require a TTY to operate, 365 * and will fail in various stupid ways if they don't get one. 366 * sshd doesn't set the tty until too late in the auth process and may 367 * not even need one (for tty-less connections) 368 * Kludge: Set a fake PAM_TTY 369 */ 370 pam_retval = pam_set_item(pamh, PAM_TTY, "ssh"); 371 if (pam_retval != PAM_SUCCESS) { 372 fatal("PAM set tty failed[%d]: %.200s", 373 pam_retval, PAM_STRERROR(pamh, pam_retval)); 374 } 375 #endif /* PAM_TTY_KLUDGE */ 376 377 fatal_add_cleanup(&do_pam_cleanup_proc, NULL); 378 } 379 380 /* Return list of PAM enviornment strings */ 381 char **fetch_pam_environment(void) 382 { 383 #ifdef HAVE_PAM_GETENVLIST 384 return(pam_getenvlist(pamh)); 385 #else /* HAVE_PAM_GETENVLIST */ 386 return(NULL); 387 #endif /* HAVE_PAM_GETENVLIST */ 388 } 389 390 /* Print any messages that have been generated during authentication */ 391 /* or account checking to stderr */ 392 void print_pam_messages(void) 393 { 394 if (pam_msg != NULL) 395 fputs(pam_msg, stderr); 396 } 397 398 /* Append a message to the PAM message buffer */ 399 void pam_msg_cat(const char *msg) 400 { 401 char *p; 402 size_t new_msg_len; 403 size_t pam_msg_len; 404 405 new_msg_len = strlen(msg); 406 407 if (pam_msg) { 408 pam_msg_len = strlen(pam_msg); 409 pam_msg = xrealloc(pam_msg, new_msg_len + pam_msg_len + 2); 410 p = pam_msg + pam_msg_len; 411 } else { 412 pam_msg = p = xmalloc(new_msg_len + 2); 413 } 414 415 memcpy(p, msg, new_msg_len); 416 p[new_msg_len] = '\n'; 417 p[new_msg_len + 1] = '\0'; 418 } 419 420 struct inverted_pam_userdata { 421 /* 422 * Pipe for telling whether we are doing conversation or sending 423 * authentication results. 424 */ 425 int statefd[2]; 426 int challengefd[2]; 427 int responsefd[2]; 428 429 /* Whether we have sent off our challenge */ 430 int state; 431 }; 432 433 #define STATE_CONV 1 434 #define STATE_AUTH_OK 2 435 #define STATE_AUTH_FAIL 3 436 437 int 438 ssh_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, 439 void *userdata) { 440 int i; 441 FILE *reader; 442 char buf[1024]; 443 struct pam_response *reply = NULL; 444 char state_to_write = STATE_CONV; /* One char to write */ 445 struct inverted_pam_userdata *ud = userdata; 446 char *response = NULL; 447 448 /* The stdio functions are more convenient for the read half */ 449 reader = fdopen(ud->responsefd[0], "rb"); 450 if (reader == NULL) 451 goto protocol_failure; 452 453 reply = malloc(num_msg * sizeof(struct pam_response)); 454 if (reply == NULL) 455 return PAM_CONV_ERR; 456 457 if (write(ud->statefd[1], &state_to_write, 1) != 1) 458 goto protocol_failure; 459 460 /* 461 * Re-package our data and send it off to our better half (the actual SSH 462 * process) 463 */ 464 if (write(ud->challengefd[1], buf, 465 sprintf(buf, "%d\n", num_msg)) == -1) 466 goto protocol_failure; 467 for (i = 0; i < num_msg; i++) { 468 if (write(ud->challengefd[1], buf, 469 sprintf(buf, "%d\n", msg[i]->msg_style)) == -1) 470 goto protocol_failure; 471 if (write(ud->challengefd[1], buf, 472 sprintf(buf, "%d\n", strlen(msg[i]->msg))) == -1) 473 goto protocol_failure; 474 if (write(ud->challengefd[1], msg[i]->msg, 475 strlen(msg[i]->msg)) == -1) 476 goto protocol_failure; 477 } 478 /* 479 * Read back responses. These may not be as nice as we want, as the SSH 480 * protocol isn't exactly a perfect fit with PAM. 481 */ 482 483 for (i = 0; i < num_msg; i++) { 484 char buf[1024]; 485 char *endptr; 486 size_t len; /* Length of the response */ 487 488 switch (msg[i]->msg_style) { 489 case PAM_PROMPT_ECHO_OFF: 490 case PAM_PROMPT_ECHO_ON: 491 if (fgets(buf, sizeof(buf), reader) == NULL) 492 goto protocol_failure; 493 len = (size_t)strtoul(buf, &endptr, 10); 494 /* The length is supposed to stand on a line by itself */ 495 if (endptr == NULL || *endptr != '\n') 496 goto protocol_failure; 497 response = malloc(len+1); 498 if (response == NULL) 499 goto protocol_failure; 500 if (fread(response, len, 1, reader) != 1) 501 goto protocol_failure; 502 response[len] = '\0'; 503 reply[i].resp = response; 504 response = NULL; 505 break; 506 default: 507 reply[i].resp = NULL; 508 break; 509 } 510 } 511 *resp = reply; 512 return PAM_SUCCESS; 513 protocol_failure: 514 free(reply); 515 return PAM_CONV_ERR; 516 } 517 518 void 519 ipam_free_cookie(struct inverted_pam_cookie *cookie) { 520 struct inverted_pam_userdata *ud; 521 int i; 522 523 if (cookie == NULL) 524 return; 525 ud = cookie->userdata; 526 cookie->userdata = NULL; 527 /* Free userdata if allocated */ 528 if (ud) { 529 /* Close any opened file descriptors */ 530 if (ud->statefd[0] != -1) 531 close(ud->statefd[0]); 532 if (ud->statefd[1] != -1) 533 close(ud->statefd[1]); 534 if (ud->challengefd[0] != -1) 535 close(ud->challengefd[0]); 536 if (ud->challengefd[1] != -1) 537 close(ud->challengefd[1]); 538 if (ud->responsefd[0] != -1) 539 close(ud->responsefd[0]); 540 if (ud->responsefd[1] != -1) 541 close(ud->responsefd[1]); 542 free(ud); 543 ud = NULL; 544 } 545 /* Now free the normal cookie */ 546 if (cookie->pid != 0 && cookie->pid != -1) { 547 int status; 548 549 /* XXX Use different signal? */ 550 kill(cookie->pid, SIGKILL); 551 waitpid(cookie->pid, &status, 0); 552 } 553 for (i = 0; i < cookie->num_msg; i++) { 554 if (cookie->resp && cookie->resp[i]) { 555 free(cookie->resp[i]->resp); 556 free(cookie->resp[i]); 557 } 558 if (cookie->msg && cookie->msg[i]) { 559 free((void *)cookie->msg[i]->msg); 560 free(cookie->msg[i]); 561 } 562 } 563 free(cookie->msg); 564 free(cookie->resp); 565 free(cookie); 566 } 567 568 /* 569 * Do first half of PAM authentication - this comes to the point where 570 * you get a message to send to the user. 571 */ 572 struct inverted_pam_cookie * 573 ipam_start_auth(const char *service, const char *username) { 574 struct inverted_pam_cookie *cookie; 575 struct inverted_pam_userdata *ud; 576 static struct pam_conv conv = { 577 ssh_conv, 578 NULL 579 }; 580 581 cookie = malloc(sizeof(*cookie)); 582 if (cookie == NULL) 583 return NULL; 584 cookie->state = 0; 585 /* Set up the cookie so ipam_freecookie can be used on it */ 586 cookie->num_msg = 0; 587 cookie->msg = NULL; 588 cookie->resp = NULL; 589 cookie->pid = -1; 590 591 ud = calloc(sizeof(*ud), 1); 592 if (ud == NULL) { 593 free(cookie); 594 return NULL; 595 } 596 cookie->userdata = ud; 597 ud->statefd[0] = ud->statefd[1] = -1; 598 ud->challengefd[0] = ud->challengefd[1] = -1; 599 ud->responsefd[0] = ud->responsefd[1] = -1; 600 601 if (pipe(ud->statefd) != 0) { 602 ud->statefd[0] = ud->statefd[1] = -1; 603 ipam_free_cookie(cookie); 604 return NULL; 605 } 606 if (pipe(ud->challengefd) != 0) { 607 ud->challengefd[0] = ud->challengefd[1] = -1; 608 ipam_free_cookie(cookie); 609 return NULL; 610 } 611 if (pipe(ud->responsefd) != 0) { 612 ud->responsefd[0] = ud->responsefd[1] = -1; 613 ipam_free_cookie(cookie); 614 return NULL; 615 } 616 cookie->pid = fork(); 617 if (cookie->pid == -1) { 618 ipam_free_cookie(cookie); 619 return NULL; 620 } else if (cookie->pid != 0) { 621 int num_msgs; /* Number of messages from PAM */ 622 char *endptr; 623 char buf[1024]; 624 FILE *reader; 625 size_t num_msg; 626 int i; 627 char state; /* Which state did the connection just enter? */ 628 629 /* We are the parent - wait for a call to the communications 630 function to turn up, or the challenge to be finished */ 631 if (read(ud->statefd[0], &state, 1) != 1) { 632 ipam_free_cookie(cookie); 633 return NULL; 634 } 635 cookie->state = state; 636 switch (state) { 637 case STATE_CONV: 638 /* We are running the conversation function */ 639 /* The stdio functions are more convenient for read */ 640 reader = fdopen(ud->challengefd[0], "r"); 641 if (reader == NULL) { 642 ipam_free_cookie(cookie); 643 return NULL; 644 } 645 if (fgets(buf, 4, reader) == NULL) { 646 fclose(reader); 647 ipam_free_cookie(cookie); 648 return NULL; 649 } 650 num_msg = (size_t)strtoul(buf, &endptr, 10); 651 /* The length is supposed to stand on a line by itself */ 652 if (endptr == NULL || *endptr != '\n') { 653 fclose(reader); 654 ipam_free_cookie(cookie); 655 return NULL; 656 } 657 cookie->msg = 658 malloc(sizeof(struct pam_message *) * num_msg); 659 cookie->resp = 660 malloc(sizeof(struct pam_response *) * num_msg); 661 if (cookie->msg == NULL || cookie->resp == NULL) { 662 fclose(reader); 663 ipam_free_cookie(cookie); 664 return NULL; 665 } 666 for (i = 0; i < num_msg; i++) { 667 cookie->msg[i] = 668 malloc(sizeof(struct pam_message)); 669 cookie->resp[i] = 670 malloc(sizeof(struct pam_response)); 671 if (cookie->msg[i] == NULL || 672 cookie->resp[i] == NULL) { 673 for (;;) { 674 free(cookie->msg[i]); 675 free(cookie->resp[i]); 676 if (i == 0) 677 break; 678 i--; 679 } 680 fclose(reader); 681 ipam_free_cookie(cookie); 682 return NULL; 683 } 684 cookie->msg[i]->msg = NULL; 685 cookie->resp[i]->resp = NULL; 686 cookie->resp[i]->resp_retcode = 0; 687 } 688 /* Set up so the above will be freed on failure */ 689 cookie->num_msg = num_msg; 690 /* 691 * We have a an allocated response and message for 692 * each of the entries in the PAM structure - transfer 693 * the data sent to the conversation function over. 694 */ 695 for (i = 0; i < num_msg; i++) { 696 size_t len; 697 698 if (fgets(buf, sizeof(buf), reader) == NULL) { 699 fclose(reader); 700 ipam_free_cookie(cookie); 701 return NULL; 702 } 703 cookie->msg[i]->msg_style = 704 (size_t)strtoul(buf, &endptr, 10); 705 if (endptr == NULL || *endptr != '\n') { 706 fclose(reader); 707 ipam_free_cookie(cookie); 708 return NULL; 709 } 710 if (fgets(buf, sizeof(buf), reader) == NULL) { 711 fclose(reader); 712 ipam_free_cookie(cookie); 713 return NULL; 714 } 715 len = (size_t)strtoul(buf, &endptr, 10); 716 if (endptr == NULL || *endptr != '\n') { 717 fclose(reader); 718 ipam_free_cookie(cookie); 719 return NULL; 720 } 721 cookie->msg[i]->msg = malloc(len + 1); 722 if (cookie->msg[i]->msg == NULL) { 723 fclose(reader); 724 ipam_free_cookie(cookie); 725 return NULL; 726 } 727 if (fread((char *)cookie->msg[i]->msg, len, 1, reader) != 728 1) { 729 fclose(reader); 730 ipam_free_cookie(cookie); 731 return NULL; 732 } 733 *(char *)&(cookie->msg[i]->msg[len]) = '\0'; 734 } 735 break; 736 case STATE_AUTH_OK: 737 case STATE_AUTH_FAIL: 738 break; 739 default: 740 /* Internal failure, somehow */ 741 fclose(reader); 742 ipam_free_cookie(cookie); 743 return NULL; 744 } 745 return cookie; 746 } else { 747 /* We are the child */ 748 pam_handle_t *pamh=NULL; 749 int retval; 750 char state; 751 752 conv.appdata_ptr = ud; 753 retval = pam_start(service, username, &conv, &pamh); 754 /* Is user really user? */ 755 if (retval == PAM_SUCCESS) 756 retval = pam_authenticate(pamh, 0); 757 /* permitted access? */ 758 if (retval == PAM_SUCCESS) 759 retval = pam_acct_mgmt(pamh, 0); 760 /* This is where we have been authorized or not. */ 761 762 /* Be conservative - flag as auth failure if we can't close */ 763 /* 764 * XXX This is based on example code from Linux-PAM - 765 * but can it really be correct to pam_end if 766 * pam_start failed? 767 */ 768 if (pam_end(pamh, retval) != PAM_SUCCESS) 769 retval = PAM_AUTH_ERR; 770 771 /* Message to parent */ 772 state = retval == PAM_SUCCESS ? STATE_AUTH_OK : STATE_AUTH_FAIL; 773 if (write(ud->statefd[1], &state, 1) != 1) { 774 _exit(1); 775 } 776 /* FDs will be closed, so further communication will stop */ 777 _exit(0); 778 } 779 } 780 781 /* 782 * Do second half of PAM authentication - cookie should now be filled 783 * in with the response to the challenge. 784 */ 785 786 int 787 ipam_complete_auth(struct inverted_pam_cookie *cookie) { 788 int i; 789 char buf[1024]; 790 struct inverted_pam_userdata *ud = cookie->userdata; 791 char state; 792 793 /* Send over our responses */ 794 for (i = 0; i < cookie->num_msg; i++) { 795 if (cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_ON && 796 cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) 797 continue; 798 if (write(ud->responsefd[1], buf, 799 sprintf(buf, "%d\n", strlen(cookie->resp[i]->resp))) == -1) { 800 ipam_free_cookie(cookie); 801 return 0; 802 } 803 if (write(ud->responsefd[1], cookie->resp[i]->resp, 804 strlen(cookie->resp[i]->resp)) == -1) { 805 ipam_free_cookie(cookie); 806 return 0; 807 } 808 } 809 /* Find out what state we are changing to */ 810 if (read(ud->statefd[0], &state, 1) != 1) { 811 ipam_free_cookie(cookie); 812 return 0; 813 } 814 815 return state == STATE_AUTH_OK ? 1 : 0; 816 } 817 818 #endif /* USE_PAM */ 819