1 /*- 2 * Copyright (c) 2002 Networks Associates Technology, Inc. 3 * All rights reserved. 4 * 5 * This software was developed for the FreeBSD Project by ThinkSec AS and 6 * NAI Labs, the Security Research Division of Network Associates, Inc. 7 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 8 * DARPA CHATS research program. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* Based on $xFreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */ 33 #include "includes.h" 34 RCSID("$Id: auth-pam.c,v 1.100 2004/04/18 01:00:26 dtucker Exp $"); 35 RCSID("$FreeBSD$"); 36 37 #ifdef USE_PAM 38 #if defined(HAVE_SECURITY_PAM_APPL_H) 39 #include <security/pam_appl.h> 40 #elif defined (HAVE_PAM_PAM_APPL_H) 41 #include <pam/pam_appl.h> 42 #endif 43 44 #include "auth.h" 45 #include "auth-pam.h" 46 #include "buffer.h" 47 #include "bufaux.h" 48 #include "canohost.h" 49 #include "log.h" 50 #include "monitor_wrap.h" 51 #include "msg.h" 52 #include "packet.h" 53 #include "readpass.h" 54 #include "servconf.h" 55 #include "ssh2.h" 56 #include "xmalloc.h" 57 #include "auth-options.h" 58 59 extern ServerOptions options; 60 extern Buffer loginmsg; 61 extern int compat20; 62 extern u_int utmp_len; 63 64 #ifdef USE_POSIX_THREADS 65 #include <pthread.h> 66 /* 67 * Avoid namespace clash when *not* using pthreads for systems *with* 68 * pthreads, which unconditionally define pthread_t via sys/types.h 69 * (e.g. Linux) 70 */ 71 typedef pthread_t sp_pthread_t; 72 #else 73 typedef pid_t sp_pthread_t; 74 #endif 75 76 struct pam_ctxt { 77 sp_pthread_t pam_thread; 78 int pam_psock; 79 int pam_csock; 80 int pam_done; 81 }; 82 83 static void sshpam_free_ctx(void *); 84 static struct pam_ctxt *cleanup_ctxt; 85 86 #ifndef USE_POSIX_THREADS 87 /* 88 * Simulate threads with processes. 89 */ 90 91 static int sshpam_thread_status = -1; 92 static mysig_t sshpam_oldsig; 93 94 static void 95 sshpam_sigchld_handler(int sig) 96 { 97 if (cleanup_ctxt == NULL) 98 return; /* handler called after PAM cleanup, shouldn't happen */ 99 if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0) == -1) 100 return; /* couldn't wait for process */ 101 if (WIFSIGNALED(sshpam_thread_status) && 102 WTERMSIG(sshpam_thread_status) == SIGTERM) 103 return; /* terminated by pthread_cancel */ 104 if (!WIFEXITED(sshpam_thread_status)) 105 fatal("PAM: authentication thread exited unexpectedly"); 106 if (WEXITSTATUS(sshpam_thread_status) != 0) 107 fatal("PAM: authentication thread exited uncleanly"); 108 } 109 110 static void 111 pthread_exit(void *value __unused) 112 { 113 _exit(0); 114 } 115 116 static int 117 pthread_create(sp_pthread_t *thread, const void *attr __unused, 118 void *(*thread_start)(void *), void *arg) 119 { 120 pid_t pid; 121 122 sshpam_thread_status = -1; 123 switch ((pid = fork())) { 124 case -1: 125 error("fork(): %s", strerror(errno)); 126 return (-1); 127 case 0: 128 thread_start(arg); 129 _exit(1); 130 default: 131 *thread = pid; 132 sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler); 133 return (0); 134 } 135 } 136 137 static int 138 pthread_cancel(sp_pthread_t thread) 139 { 140 signal(SIGCHLD, sshpam_oldsig); 141 return (kill(thread, SIGTERM)); 142 } 143 144 static int 145 pthread_join(sp_pthread_t thread, void **value __unused) 146 { 147 int status; 148 149 if (sshpam_thread_status != -1) 150 return (sshpam_thread_status); 151 signal(SIGCHLD, sshpam_oldsig); 152 waitpid(thread, &status, 0); 153 return (status); 154 } 155 #endif 156 157 158 static pam_handle_t *sshpam_handle = NULL; 159 static int sshpam_err = 0; 160 static int sshpam_authenticated = 0; 161 static int sshpam_session_open = 0; 162 static int sshpam_cred_established = 0; 163 static int sshpam_account_status = -1; 164 static char **sshpam_env = NULL; 165 static Authctxt *sshpam_authctxt = NULL; 166 167 /* Some PAM implementations don't implement this */ 168 #ifndef HAVE_PAM_GETENVLIST 169 static char ** 170 pam_getenvlist(pam_handle_t *pamh) 171 { 172 /* 173 * XXX - If necessary, we can still support envrionment passing 174 * for platforms without pam_getenvlist by searching for known 175 * env vars (e.g. KRB5CCNAME) from the PAM environment. 176 */ 177 return NULL; 178 } 179 #endif 180 181 void 182 pam_password_change_required(int reqd) 183 { 184 debug3("%s %d", __func__, reqd); 185 if (sshpam_authctxt == NULL) 186 fatal("%s: PAM authctxt not initialized", __func__); 187 sshpam_authctxt->force_pwchange = reqd; 188 if (reqd) { 189 no_port_forwarding_flag |= 2; 190 no_agent_forwarding_flag |= 2; 191 no_x11_forwarding_flag |= 2; 192 } else { 193 no_port_forwarding_flag &= ~2; 194 no_agent_forwarding_flag &= ~2; 195 no_x11_forwarding_flag &= ~2; 196 } 197 } 198 199 /* Import regular and PAM environment from subprocess */ 200 static void 201 import_environments(Buffer *b) 202 { 203 char *env; 204 u_int i, num_env; 205 int err; 206 207 debug3("PAM: %s entering", __func__); 208 209 #ifndef USE_POSIX_THREADS 210 /* Import variables set by do_pam_account */ 211 sshpam_account_status = buffer_get_int(b); 212 pam_password_change_required(buffer_get_int(b)); 213 214 /* Import environment from subprocess */ 215 num_env = buffer_get_int(b); 216 sshpam_env = xmalloc((num_env + 1) * sizeof(*sshpam_env)); 217 debug3("PAM: num env strings %d", num_env); 218 for(i = 0; i < num_env; i++) 219 sshpam_env[i] = buffer_get_string(b, NULL); 220 221 sshpam_env[num_env] = NULL; 222 223 /* Import PAM environment from subprocess */ 224 num_env = buffer_get_int(b); 225 debug("PAM: num PAM env strings %d", num_env); 226 for(i = 0; i < num_env; i++) { 227 env = buffer_get_string(b, NULL); 228 229 #ifdef HAVE_PAM_PUTENV 230 /* Errors are not fatal here */ 231 if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) { 232 error("PAM: pam_putenv: %s", 233 pam_strerror(sshpam_handle, sshpam_err)); 234 } 235 #endif 236 } 237 #endif 238 } 239 240 /* 241 * Conversation function for authentication thread. 242 */ 243 static int 244 sshpam_thread_conv(int n, const struct pam_message **msg, 245 struct pam_response **resp, void *data) 246 { 247 Buffer buffer; 248 struct pam_ctxt *ctxt; 249 struct pam_response *reply; 250 int i; 251 252 debug3("PAM: %s entering, %d messages", __func__, n); 253 *resp = NULL; 254 255 ctxt = data; 256 if (n <= 0 || n > PAM_MAX_NUM_MSG) 257 return (PAM_CONV_ERR); 258 259 if ((reply = malloc(n * sizeof(*reply))) == NULL) 260 return (PAM_CONV_ERR); 261 memset(reply, 0, n * sizeof(*reply)); 262 263 buffer_init(&buffer); 264 for (i = 0; i < n; ++i) { 265 switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 266 case PAM_PROMPT_ECHO_OFF: 267 buffer_put_cstring(&buffer, 268 PAM_MSG_MEMBER(msg, i, msg)); 269 if (ssh_msg_send(ctxt->pam_csock, 270 PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) 271 goto fail; 272 if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1) 273 goto fail; 274 if (buffer_get_char(&buffer) != PAM_AUTHTOK) 275 goto fail; 276 reply[i].resp = buffer_get_string(&buffer, NULL); 277 break; 278 case PAM_PROMPT_ECHO_ON: 279 buffer_put_cstring(&buffer, 280 PAM_MSG_MEMBER(msg, i, msg)); 281 if (ssh_msg_send(ctxt->pam_csock, 282 PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) 283 goto fail; 284 if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1) 285 goto fail; 286 if (buffer_get_char(&buffer) != PAM_AUTHTOK) 287 goto fail; 288 reply[i].resp = buffer_get_string(&buffer, NULL); 289 break; 290 case PAM_ERROR_MSG: 291 buffer_put_cstring(&buffer, 292 PAM_MSG_MEMBER(msg, i, msg)); 293 if (ssh_msg_send(ctxt->pam_csock, 294 PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) 295 goto fail; 296 break; 297 case PAM_TEXT_INFO: 298 buffer_put_cstring(&buffer, 299 PAM_MSG_MEMBER(msg, i, msg)); 300 if (ssh_msg_send(ctxt->pam_csock, 301 PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) 302 goto fail; 303 break; 304 default: 305 goto fail; 306 } 307 buffer_clear(&buffer); 308 } 309 buffer_free(&buffer); 310 *resp = reply; 311 return (PAM_SUCCESS); 312 313 fail: 314 for(i = 0; i < n; i++) { 315 if (reply[i].resp != NULL) 316 xfree(reply[i].resp); 317 } 318 xfree(reply); 319 buffer_free(&buffer); 320 return (PAM_CONV_ERR); 321 } 322 323 /* 324 * Authentication thread. 325 */ 326 static void * 327 sshpam_thread(void *ctxtp) 328 { 329 struct pam_ctxt *ctxt = ctxtp; 330 Buffer buffer; 331 struct pam_conv sshpam_conv; 332 #ifndef USE_POSIX_THREADS 333 extern char **environ; 334 char **env_from_pam; 335 u_int i; 336 const char *pam_user; 337 338 pam_get_item(sshpam_handle, PAM_USER, (const void **)&pam_user); 339 setproctitle("%s [pam]", pam_user); 340 environ[0] = NULL; 341 #endif 342 343 sshpam_conv.conv = sshpam_thread_conv; 344 sshpam_conv.appdata_ptr = ctxt; 345 346 if (sshpam_authctxt == NULL) 347 fatal("%s: PAM authctxt not initialized", __func__); 348 349 buffer_init(&buffer); 350 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 351 (const void *)&sshpam_conv); 352 if (sshpam_err != PAM_SUCCESS) 353 goto auth_fail; 354 sshpam_err = pam_authenticate(sshpam_handle, 0); 355 if (sshpam_err != PAM_SUCCESS) 356 goto auth_fail; 357 358 if (compat20) { 359 if (!do_pam_account()) 360 goto auth_fail; 361 if (sshpam_authctxt->force_pwchange) { 362 sshpam_err = pam_chauthtok(sshpam_handle, 363 PAM_CHANGE_EXPIRED_AUTHTOK); 364 if (sshpam_err != PAM_SUCCESS) 365 goto auth_fail; 366 pam_password_change_required(0); 367 } 368 } 369 370 buffer_put_cstring(&buffer, "OK"); 371 372 #ifndef USE_POSIX_THREADS 373 /* Export variables set by do_pam_account */ 374 buffer_put_int(&buffer, sshpam_account_status); 375 buffer_put_int(&buffer, sshpam_authctxt->force_pwchange); 376 377 /* Export any environment strings set in child */ 378 for(i = 0; environ[i] != NULL; i++) 379 ; /* Count */ 380 buffer_put_int(&buffer, i); 381 for(i = 0; environ[i] != NULL; i++) 382 buffer_put_cstring(&buffer, environ[i]); 383 384 /* Export any environment strings set by PAM in child */ 385 env_from_pam = pam_getenvlist(sshpam_handle); 386 for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) 387 ; /* Count */ 388 buffer_put_int(&buffer, i); 389 for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) 390 buffer_put_cstring(&buffer, env_from_pam[i]); 391 #endif /* USE_POSIX_THREADS */ 392 393 /* XXX - can't do much about an error here */ 394 ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer); 395 buffer_free(&buffer); 396 pthread_exit(NULL); 397 398 auth_fail: 399 buffer_put_cstring(&buffer, 400 pam_strerror(sshpam_handle, sshpam_err)); 401 /* XXX - can't do much about an error here */ 402 ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer); 403 buffer_free(&buffer); 404 pthread_exit(NULL); 405 406 return (NULL); /* Avoid warning for non-pthread case */ 407 } 408 409 void 410 sshpam_thread_cleanup(void) 411 { 412 struct pam_ctxt *ctxt = cleanup_ctxt; 413 414 debug3("PAM: %s entering", __func__); 415 if (ctxt != NULL && ctxt->pam_thread != 0) { 416 pthread_cancel(ctxt->pam_thread); 417 pthread_join(ctxt->pam_thread, NULL); 418 close(ctxt->pam_psock); 419 close(ctxt->pam_csock); 420 memset(ctxt, 0, sizeof(*ctxt)); 421 cleanup_ctxt = NULL; 422 } 423 } 424 425 static int 426 sshpam_null_conv(int n, const struct pam_message **msg, 427 struct pam_response **resp, void *data) 428 { 429 debug3("PAM: %s entering, %d messages", __func__, n); 430 return (PAM_CONV_ERR); 431 } 432 433 static struct pam_conv null_conv = { sshpam_null_conv, NULL }; 434 435 void 436 sshpam_cleanup(void) 437 { 438 debug("PAM: cleanup"); 439 if (sshpam_handle == NULL) 440 return; 441 pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv); 442 if (sshpam_cred_established) { 443 pam_setcred(sshpam_handle, PAM_DELETE_CRED); 444 sshpam_cred_established = 0; 445 } 446 if (sshpam_session_open) { 447 pam_close_session(sshpam_handle, PAM_SILENT); 448 sshpam_session_open = 0; 449 } 450 sshpam_authenticated = 0; 451 pam_end(sshpam_handle, sshpam_err); 452 sshpam_handle = NULL; 453 } 454 455 static int 456 sshpam_init(Authctxt *authctxt) 457 { 458 extern char *__progname; 459 const char *pam_rhost, *pam_user, *user = authctxt->user; 460 461 if (sshpam_handle != NULL) { 462 /* We already have a PAM context; check if the user matches */ 463 sshpam_err = pam_get_item(sshpam_handle, 464 PAM_USER, (const void **)&pam_user); 465 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0) 466 return (0); 467 pam_end(sshpam_handle, sshpam_err); 468 sshpam_handle = NULL; 469 } 470 debug("PAM: initializing for \"%s\"", user); 471 sshpam_err = 472 pam_start(SSHD_PAM_SERVICE, user, &null_conv, &sshpam_handle); 473 sshpam_authctxt = authctxt; 474 475 if (sshpam_err != PAM_SUCCESS) { 476 pam_end(sshpam_handle, sshpam_err); 477 sshpam_handle = NULL; 478 return (-1); 479 } 480 pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns); 481 debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost); 482 sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost); 483 if (sshpam_err != PAM_SUCCESS) { 484 pam_end(sshpam_handle, sshpam_err); 485 sshpam_handle = NULL; 486 return (-1); 487 } 488 #ifdef PAM_TTY_KLUDGE 489 /* 490 * Some silly PAM modules (e.g. pam_time) require a TTY to operate. 491 * sshd doesn't set the tty until too late in the auth process and 492 * may not even set one (for tty-less connections) 493 */ 494 debug("PAM: setting PAM_TTY to \"ssh\""); 495 sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh"); 496 if (sshpam_err != PAM_SUCCESS) { 497 pam_end(sshpam_handle, sshpam_err); 498 sshpam_handle = NULL; 499 return (-1); 500 } 501 #endif 502 return (0); 503 } 504 505 static void * 506 sshpam_init_ctx(Authctxt *authctxt) 507 { 508 struct pam_ctxt *ctxt; 509 int socks[2]; 510 511 debug3("PAM: %s entering", __func__); 512 /* Refuse to start if we don't have PAM enabled */ 513 if (!options.use_pam) 514 return NULL; 515 516 /* Initialize PAM */ 517 if (sshpam_init(authctxt) == -1) { 518 error("PAM: initialization failed"); 519 return (NULL); 520 } 521 522 ctxt = xmalloc(sizeof *ctxt); 523 memset(ctxt, 0, sizeof(*ctxt)); 524 525 /* Start the authentication thread */ 526 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) { 527 error("PAM: failed create sockets: %s", strerror(errno)); 528 xfree(ctxt); 529 return (NULL); 530 } 531 ctxt->pam_psock = socks[0]; 532 ctxt->pam_csock = socks[1]; 533 if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) { 534 error("PAM: failed to start authentication thread: %s", 535 strerror(errno)); 536 close(socks[0]); 537 close(socks[1]); 538 xfree(ctxt); 539 return (NULL); 540 } 541 cleanup_ctxt = ctxt; 542 return (ctxt); 543 } 544 545 static int 546 sshpam_query(void *ctx, char **name, char **info, 547 u_int *num, char ***prompts, u_int **echo_on) 548 { 549 Buffer buffer; 550 struct pam_ctxt *ctxt = ctx; 551 size_t plen; 552 u_char type; 553 char *msg; 554 size_t len; 555 556 debug3("PAM: %s entering", __func__); 557 buffer_init(&buffer); 558 *name = xstrdup(""); 559 *info = xstrdup(""); 560 *prompts = xmalloc(sizeof(char *)); 561 **prompts = NULL; 562 plen = 0; 563 *echo_on = xmalloc(sizeof(u_int)); 564 while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) { 565 type = buffer_get_char(&buffer); 566 msg = buffer_get_string(&buffer, NULL); 567 switch (type) { 568 case PAM_PROMPT_ECHO_ON: 569 case PAM_PROMPT_ECHO_OFF: 570 *num = 1; 571 len = plen + strlen(msg) + 1; 572 **prompts = xrealloc(**prompts, len); 573 plen += snprintf(**prompts + plen, len, "%s", msg); 574 **echo_on = (type == PAM_PROMPT_ECHO_ON); 575 xfree(msg); 576 return (0); 577 case PAM_ERROR_MSG: 578 case PAM_TEXT_INFO: 579 /* accumulate messages */ 580 len = plen + strlen(msg) + 2; 581 **prompts = xrealloc(**prompts, len); 582 plen += snprintf(**prompts + plen, len, "%s\n", msg); 583 xfree(msg); 584 break; 585 case PAM_SUCCESS: 586 case PAM_AUTH_ERR: 587 if (**prompts != NULL) { 588 /* drain any accumulated messages */ 589 debug("PAM: %s", **prompts); 590 buffer_append(&loginmsg, **prompts, 591 strlen(**prompts)); 592 xfree(**prompts); 593 **prompts = NULL; 594 } 595 if (type == PAM_SUCCESS) { 596 import_environments(&buffer); 597 *num = 0; 598 **echo_on = 0; 599 ctxt->pam_done = 1; 600 xfree(msg); 601 return (0); 602 } 603 error("PAM: %s for %s%.100s from %.100s", msg, 604 sshpam_authctxt->valid ? "" : "illegal user ", 605 sshpam_authctxt->user, 606 get_remote_name_or_ip(utmp_len, options.use_dns)); 607 /* FALLTHROUGH */ 608 default: 609 *num = 0; 610 **echo_on = 0; 611 xfree(msg); 612 ctxt->pam_done = -1; 613 return (-1); 614 } 615 } 616 return (-1); 617 } 618 619 /* XXX - see also comment in auth-chall.c:verify_response */ 620 static int 621 sshpam_respond(void *ctx, u_int num, char **resp) 622 { 623 Buffer buffer; 624 struct pam_ctxt *ctxt = ctx; 625 626 debug2("PAM: %s entering, %d responses", __func__, num); 627 switch (ctxt->pam_done) { 628 case 1: 629 sshpam_authenticated = 1; 630 return (0); 631 case 0: 632 break; 633 default: 634 return (-1); 635 } 636 if (num != 1) { 637 error("PAM: expected one response, got %u", num); 638 return (-1); 639 } 640 buffer_init(&buffer); 641 buffer_put_cstring(&buffer, *resp); 642 if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) { 643 buffer_free(&buffer); 644 return (-1); 645 } 646 buffer_free(&buffer); 647 return (1); 648 } 649 650 static void 651 sshpam_free_ctx(void *ctxtp) 652 { 653 struct pam_ctxt *ctxt = ctxtp; 654 655 debug3("PAM: %s entering", __func__); 656 sshpam_thread_cleanup(); 657 xfree(ctxt); 658 /* 659 * We don't call sshpam_cleanup() here because we may need the PAM 660 * handle at a later stage, e.g. when setting up a session. It's 661 * still on the cleanup list, so pam_end() *will* be called before 662 * the server process terminates. 663 */ 664 } 665 666 KbdintDevice sshpam_device = { 667 "pam", 668 sshpam_init_ctx, 669 sshpam_query, 670 sshpam_respond, 671 sshpam_free_ctx 672 }; 673 674 KbdintDevice mm_sshpam_device = { 675 "pam", 676 mm_sshpam_init_ctx, 677 mm_sshpam_query, 678 mm_sshpam_respond, 679 mm_sshpam_free_ctx 680 }; 681 682 /* 683 * This replaces auth-pam.c 684 */ 685 void 686 start_pam(Authctxt *authctxt) 687 { 688 if (!options.use_pam) 689 fatal("PAM: initialisation requested when UsePAM=no"); 690 691 if (sshpam_init(authctxt) == -1) 692 fatal("PAM: initialisation failed"); 693 } 694 695 void 696 finish_pam(void) 697 { 698 sshpam_cleanup(); 699 } 700 701 u_int 702 do_pam_account(void) 703 { 704 if (sshpam_account_status != -1) 705 return (sshpam_account_status); 706 707 sshpam_err = pam_acct_mgmt(sshpam_handle, 0); 708 debug3("PAM: %s pam_acct_mgmt = %d", __func__, sshpam_err); 709 710 if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) { 711 sshpam_account_status = 0; 712 return (sshpam_account_status); 713 } 714 715 if (sshpam_err == PAM_NEW_AUTHTOK_REQD) 716 pam_password_change_required(1); 717 718 sshpam_account_status = 1; 719 return (sshpam_account_status); 720 } 721 722 void 723 do_pam_set_tty(const char *tty) 724 { 725 if (tty != NULL) { 726 debug("PAM: setting PAM_TTY to \"%s\"", tty); 727 sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty); 728 if (sshpam_err != PAM_SUCCESS) 729 fatal("PAM: failed to set PAM_TTY: %s", 730 pam_strerror(sshpam_handle, sshpam_err)); 731 } 732 } 733 734 void 735 do_pam_setcred(int init) 736 { 737 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 738 (const void *)&null_conv); 739 if (sshpam_err != PAM_SUCCESS) 740 fatal("PAM: failed to set PAM_CONV: %s", 741 pam_strerror(sshpam_handle, sshpam_err)); 742 if (init) { 743 debug("PAM: establishing credentials"); 744 sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED); 745 } else { 746 debug("PAM: reinitializing credentials"); 747 sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED); 748 } 749 if (sshpam_err == PAM_SUCCESS) { 750 sshpam_cred_established = 1; 751 return; 752 } 753 if (sshpam_authenticated) 754 fatal("PAM: pam_setcred(): %s", 755 pam_strerror(sshpam_handle, sshpam_err)); 756 else 757 debug("PAM: pam_setcred(): %s", 758 pam_strerror(sshpam_handle, sshpam_err)); 759 } 760 761 static int 762 pam_tty_conv(int n, const struct pam_message **msg, 763 struct pam_response **resp, void *data) 764 { 765 char input[PAM_MAX_MSG_SIZE]; 766 struct pam_response *reply; 767 int i; 768 769 debug3("PAM: %s called with %d messages", __func__, n); 770 771 *resp = NULL; 772 773 if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO)) 774 return (PAM_CONV_ERR); 775 776 if ((reply = malloc(n * sizeof(*reply))) == NULL) 777 return (PAM_CONV_ERR); 778 memset(reply, 0, n * sizeof(*reply)); 779 780 for (i = 0; i < n; ++i) { 781 switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 782 case PAM_PROMPT_ECHO_OFF: 783 reply[i].resp = 784 read_passphrase(PAM_MSG_MEMBER(msg, i, msg), 785 RP_ALLOW_STDIN); 786 reply[i].resp_retcode = PAM_SUCCESS; 787 break; 788 case PAM_PROMPT_ECHO_ON: 789 fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 790 fgets(input, sizeof input, stdin); 791 reply[i].resp = xstrdup(input); 792 reply[i].resp_retcode = PAM_SUCCESS; 793 break; 794 case PAM_ERROR_MSG: 795 case PAM_TEXT_INFO: 796 fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 797 reply[i].resp_retcode = PAM_SUCCESS; 798 break; 799 default: 800 goto fail; 801 } 802 } 803 *resp = reply; 804 return (PAM_SUCCESS); 805 806 fail: 807 for(i = 0; i < n; i++) { 808 if (reply[i].resp != NULL) 809 xfree(reply[i].resp); 810 } 811 xfree(reply); 812 return (PAM_CONV_ERR); 813 } 814 815 static struct pam_conv tty_conv = { pam_tty_conv, NULL }; 816 817 /* 818 * XXX this should be done in the authentication phase, but ssh1 doesn't 819 * support that 820 */ 821 void 822 do_pam_chauthtok(void) 823 { 824 if (use_privsep) 825 fatal("Password expired (unable to change with privsep)"); 826 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 827 (const void *)&tty_conv); 828 if (sshpam_err != PAM_SUCCESS) 829 fatal("PAM: failed to set PAM_CONV: %s", 830 pam_strerror(sshpam_handle, sshpam_err)); 831 debug("PAM: changing password"); 832 sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK); 833 if (sshpam_err != PAM_SUCCESS) 834 fatal("PAM: pam_chauthtok(): %s", 835 pam_strerror(sshpam_handle, sshpam_err)); 836 } 837 838 static int 839 pam_store_conv(int n, const struct pam_message **msg, 840 struct pam_response **resp, void *data) 841 { 842 struct pam_response *reply; 843 int i; 844 size_t len; 845 846 debug3("PAM: %s called with %d messages", __func__, n); 847 *resp = NULL; 848 849 if (n <= 0 || n > PAM_MAX_NUM_MSG) 850 return (PAM_CONV_ERR); 851 852 if ((reply = malloc(n * sizeof(*reply))) == NULL) 853 return (PAM_CONV_ERR); 854 memset(reply, 0, n * sizeof(*reply)); 855 856 for (i = 0; i < n; ++i) { 857 switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 858 case PAM_ERROR_MSG: 859 case PAM_TEXT_INFO: 860 len = strlen(PAM_MSG_MEMBER(msg, i, msg)); 861 buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len); 862 buffer_append(&loginmsg, "\n", 1 ); 863 reply[i].resp_retcode = PAM_SUCCESS; 864 break; 865 default: 866 goto fail; 867 } 868 } 869 *resp = reply; 870 return (PAM_SUCCESS); 871 872 fail: 873 for(i = 0; i < n; i++) { 874 if (reply[i].resp != NULL) 875 xfree(reply[i].resp); 876 } 877 xfree(reply); 878 return (PAM_CONV_ERR); 879 } 880 881 static struct pam_conv store_conv = { pam_store_conv, NULL }; 882 883 void 884 do_pam_session(void) 885 { 886 debug3("PAM: opening session"); 887 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 888 (const void *)&store_conv); 889 if (sshpam_err != PAM_SUCCESS) 890 fatal("PAM: failed to set PAM_CONV: %s", 891 pam_strerror(sshpam_handle, sshpam_err)); 892 sshpam_err = pam_open_session(sshpam_handle, 0); 893 if (sshpam_err != PAM_SUCCESS) 894 fatal("PAM: pam_open_session(): %s", 895 pam_strerror(sshpam_handle, sshpam_err)); 896 sshpam_session_open = 1; 897 } 898 899 /* 900 * Set a PAM environment string. We need to do this so that the session 901 * modules can handle things like Kerberos/GSI credentials that appear 902 * during the ssh authentication process. 903 */ 904 int 905 do_pam_putenv(char *name, char *value) 906 { 907 int ret = 1; 908 #ifdef HAVE_PAM_PUTENV 909 char *compound; 910 size_t len; 911 912 len = strlen(name) + strlen(value) + 2; 913 compound = xmalloc(len); 914 915 snprintf(compound, len, "%s=%s", name, value); 916 ret = pam_putenv(sshpam_handle, compound); 917 xfree(compound); 918 #endif 919 920 return (ret); 921 } 922 923 char ** 924 fetch_pam_child_environment(void) 925 { 926 return sshpam_env; 927 } 928 929 char ** 930 fetch_pam_environment(void) 931 { 932 return (pam_getenvlist(sshpam_handle)); 933 } 934 935 void 936 free_pam_environment(char **env) 937 { 938 char **envp; 939 940 if (env == NULL) 941 return; 942 943 for (envp = env; *envp; envp++) 944 xfree(*envp); 945 xfree(env); 946 } 947 948 #endif /* USE_PAM */ 949