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.verify_reverse_mapping)); 227 pam_retval = pam_set_item(pamh, PAM_RHOST, 228 get_canonical_hostname(options.verify_reverse_mapping)); 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 const char *rhost; 581 582 cookie = malloc(sizeof(*cookie)); 583 if (cookie == NULL) 584 return NULL; 585 cookie->state = 0; 586 /* Set up the cookie so ipam_freecookie can be used on it */ 587 cookie->num_msg = 0; 588 cookie->msg = NULL; 589 cookie->resp = NULL; 590 cookie->pid = -1; 591 592 ud = calloc(sizeof(*ud), 1); 593 if (ud == NULL) { 594 free(cookie); 595 return NULL; 596 } 597 cookie->userdata = ud; 598 ud->statefd[0] = ud->statefd[1] = -1; 599 ud->challengefd[0] = ud->challengefd[1] = -1; 600 ud->responsefd[0] = ud->responsefd[1] = -1; 601 602 if (pipe(ud->statefd) != 0) { 603 ud->statefd[0] = ud->statefd[1] = -1; 604 ipam_free_cookie(cookie); 605 return NULL; 606 } 607 if (pipe(ud->challengefd) != 0) { 608 ud->challengefd[0] = ud->challengefd[1] = -1; 609 ipam_free_cookie(cookie); 610 return NULL; 611 } 612 if (pipe(ud->responsefd) != 0) { 613 ud->responsefd[0] = ud->responsefd[1] = -1; 614 ipam_free_cookie(cookie); 615 return NULL; 616 } 617 rhost = get_canonical_hostname(options.verify_reverse_mapping); 618 cookie->pid = fork(); 619 if (cookie->pid == -1) { 620 ipam_free_cookie(cookie); 621 return NULL; 622 } else if (cookie->pid != 0) { 623 int num_msgs; /* Number of messages from PAM */ 624 char *endptr; 625 char buf[1024]; 626 FILE *reader; 627 size_t num_msg; 628 int i; 629 char state; /* Which state did the connection just enter? */ 630 631 /* We are the parent - wait for a call to the communications 632 function to turn up, or the challenge to be finished */ 633 if (read(ud->statefd[0], &state, 1) != 1) { 634 ipam_free_cookie(cookie); 635 return NULL; 636 } 637 cookie->state = state; 638 switch (state) { 639 case STATE_CONV: 640 /* We are running the conversation function */ 641 /* The stdio functions are more convenient for read */ 642 reader = fdopen(ud->challengefd[0], "r"); 643 if (reader == NULL) { 644 ipam_free_cookie(cookie); 645 return NULL; 646 } 647 if (fgets(buf, 4, reader) == NULL) { 648 fclose(reader); 649 ipam_free_cookie(cookie); 650 return NULL; 651 } 652 num_msg = (size_t)strtoul(buf, &endptr, 10); 653 /* The length is supposed to stand on a line by itself */ 654 if (endptr == NULL || *endptr != '\n') { 655 fclose(reader); 656 ipam_free_cookie(cookie); 657 return NULL; 658 } 659 cookie->msg = 660 malloc(sizeof(struct pam_message *) * num_msg); 661 cookie->resp = 662 malloc(sizeof(struct pam_response *) * num_msg); 663 if (cookie->msg == NULL || cookie->resp == NULL) { 664 fclose(reader); 665 ipam_free_cookie(cookie); 666 return NULL; 667 } 668 for (i = 0; i < num_msg; i++) { 669 cookie->msg[i] = 670 malloc(sizeof(struct pam_message)); 671 cookie->resp[i] = 672 malloc(sizeof(struct pam_response)); 673 if (cookie->msg[i] == NULL || 674 cookie->resp[i] == NULL) { 675 for (;;) { 676 free(cookie->msg[i]); 677 free(cookie->resp[i]); 678 if (i == 0) 679 break; 680 i--; 681 } 682 fclose(reader); 683 ipam_free_cookie(cookie); 684 return NULL; 685 } 686 cookie->msg[i]->msg = NULL; 687 cookie->resp[i]->resp = NULL; 688 cookie->resp[i]->resp_retcode = 0; 689 } 690 /* Set up so the above will be freed on failure */ 691 cookie->num_msg = num_msg; 692 /* 693 * We have a an allocated response and message for 694 * each of the entries in the PAM structure - transfer 695 * the data sent to the conversation function over. 696 */ 697 for (i = 0; i < num_msg; i++) { 698 size_t len; 699 700 if (fgets(buf, sizeof(buf), reader) == NULL) { 701 fclose(reader); 702 ipam_free_cookie(cookie); 703 return NULL; 704 } 705 cookie->msg[i]->msg_style = 706 (size_t)strtoul(buf, &endptr, 10); 707 if (endptr == NULL || *endptr != '\n') { 708 fclose(reader); 709 ipam_free_cookie(cookie); 710 return NULL; 711 } 712 if (fgets(buf, sizeof(buf), reader) == NULL) { 713 fclose(reader); 714 ipam_free_cookie(cookie); 715 return NULL; 716 } 717 len = (size_t)strtoul(buf, &endptr, 10); 718 if (endptr == NULL || *endptr != '\n') { 719 fclose(reader); 720 ipam_free_cookie(cookie); 721 return NULL; 722 } 723 cookie->msg[i]->msg = malloc(len + 1); 724 if (cookie->msg[i]->msg == NULL) { 725 fclose(reader); 726 ipam_free_cookie(cookie); 727 return NULL; 728 } 729 if (fread((char *)cookie->msg[i]->msg, len, 1, reader) != 730 1) { 731 fclose(reader); 732 ipam_free_cookie(cookie); 733 return NULL; 734 } 735 *(char *)&(cookie->msg[i]->msg[len]) = '\0'; 736 } 737 break; 738 case STATE_AUTH_OK: 739 case STATE_AUTH_FAIL: 740 break; 741 default: 742 /* Internal failure, somehow */ 743 fclose(reader); 744 ipam_free_cookie(cookie); 745 return NULL; 746 } 747 return cookie; 748 } else { 749 /* We are the child */ 750 pam_handle_t *pamh=NULL; 751 int retval; 752 char state; 753 754 conv.appdata_ptr = ud; 755 retval = pam_start(service, username, &conv, &pamh); 756 fprintf(stderr, "pam_start returned %d\n", retval); 757 if (retval == PAM_SUCCESS) 758 retval = pam_set_item(pamh, PAM_RHOST, rhost); 759 /* Is user really user? */ 760 if (retval == PAM_SUCCESS) 761 retval = pam_authenticate(pamh, 0); 762 /* permitted access? */ 763 if (retval == PAM_SUCCESS) 764 retval = pam_acct_mgmt(pamh, 0); 765 /* This is where we have been authorized or not. */ 766 767 /* Be conservative - flag as auth failure if we can't close */ 768 /* 769 * XXX This is based on example code from Linux-PAM - 770 * but can it really be correct to pam_end if 771 * pam_start failed? 772 */ 773 if (pam_end(pamh, retval) != PAM_SUCCESS) 774 retval = PAM_AUTH_ERR; 775 776 /* Message to parent */ 777 state = retval == PAM_SUCCESS ? STATE_AUTH_OK : STATE_AUTH_FAIL; 778 if (write(ud->statefd[1], &state, 1) != 1) { 779 _exit(1); 780 } 781 /* FDs will be closed, so further communication will stop */ 782 _exit(0); 783 } 784 } 785 786 /* 787 * Do second half of PAM authentication - cookie should now be filled 788 * in with the response to the challenge. 789 */ 790 791 int 792 ipam_complete_auth(struct inverted_pam_cookie *cookie) { 793 int i; 794 char buf[1024]; 795 struct inverted_pam_userdata *ud = cookie->userdata; 796 char state; 797 798 /* Send over our responses */ 799 for (i = 0; i < cookie->num_msg; i++) { 800 if (cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_ON && 801 cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) 802 continue; 803 if (write(ud->responsefd[1], buf, 804 sprintf(buf, "%d\n", strlen(cookie->resp[i]->resp))) == -1) { 805 ipam_free_cookie(cookie); 806 return 0; 807 } 808 if (write(ud->responsefd[1], cookie->resp[i]->resp, 809 strlen(cookie->resp[i]->resp)) == -1) { 810 ipam_free_cookie(cookie); 811 return 0; 812 } 813 } 814 /* Find out what state we are changing to */ 815 if (read(ud->statefd[0], &state, 1) != 1) { 816 ipam_free_cookie(cookie); 817 return 0; 818 } 819 820 return state == STATE_AUTH_OK ? 1 : 0; 821 } 822 823 #endif /* USE_PAM */ 824