1 /* $NetBSD: auth-pam.c,v 1.25 2025/03/26 01:33:02 htodd Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 Networks Associates Technology, Inc. 5 * All rights reserved. 6 * 7 * This software was developed for the FreeBSD Project by ThinkSec AS and 8 * NAI Labs, the Security Research Division of Network Associates, Inc. 9 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 10 * DARPA CHATS research program. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 /* 34 * Copyright (c) 2003,2004 Damien Miller <djm (at) mindrot.org> 35 * Copyright (c) 2003,2004 Darren Tucker <dtucker (at) zip.com.au> 36 * 37 * Permission to use, copy, modify, and distribute this software for any 38 * purpose with or without fee is hereby granted, provided that the above 39 * copyright notice and this permission notice appear in all copies. 40 * 41 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 42 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 43 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 44 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 45 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 46 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 47 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 48 */ 49 50 /* Based on FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des */ 51 52 #include "includes.h" 53 /* 54 * NetBSD local changes 55 */ 56 __RCSID("$NetBSD: auth-pam.c,v 1.25 2025/03/26 01:33:02 htodd Exp $"); 57 #define _LIB_PTHREAD_H 58 #undef USE_POSIX_THREADS /* Not yet */ 59 #define HAVE_SECURITY_PAM_APPL_H 60 #define HAVE_PAM_GETENVLIST 61 #define HAVE_PAM_PUTENV 62 #define sshpam_const const /* LinuxPAM, OpenPAM */ 63 #define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member) 64 #define SSHD_PAM_SERVICE getprogname() 65 /* end NetBSD local changes */ 66 67 #include <sys/types.h> 68 #include <sys/socket.h> 69 #include <sys/stat.h> 70 #include <sys/wait.h> 71 72 #include <errno.h> 73 #include <signal.h> 74 #include <stdarg.h> 75 #include <stdlib.h> 76 #include <string.h> 77 #include <unistd.h> 78 #include <pwd.h> 79 80 #ifdef USE_PAM 81 #if defined(HAVE_SECURITY_PAM_APPL_H) 82 #include <security/pam_appl.h> 83 #elif defined (HAVE_PAM_PAM_APPL_H) 84 #include <pam/pam_appl.h> 85 #endif 86 87 #ifndef __NetBSD__ 88 /* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */ 89 #ifdef PAM_SUN_CODEBASE 90 # define sshpam_const /* Solaris, HP-UX, SunOS */ 91 #else 92 # define sshpam_const const /* LinuxPAM, OpenPAM, AIX */ 93 #endif 94 95 /* Ambiguity in spec: is it an array of pointers or a pointer to an array? */ 96 #ifdef PAM_SUN_CODEBASE 97 # define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member) 98 #else 99 # define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member) 100 #endif 101 #endif 102 103 #include "xmalloc.h" 104 #include "sshbuf.h" 105 #include "ssherr.h" 106 #include "hostfile.h" 107 #include "auth.h" 108 #include "auth-pam.h" 109 #include "canohost.h" 110 #include "log.h" 111 #include "msg.h" 112 #include "packet.h" 113 #include "misc.h" 114 #include "pfilter.h" 115 #include "servconf.h" 116 #include "srclimit.h" 117 #include "ssh2.h" 118 #include "auth-options.h" 119 #ifdef GSSAPI 120 #include "ssh-gss.h" 121 #endif 122 #include "monitor_wrap.h" 123 124 extern ServerOptions options; 125 extern struct sshbuf *loginmsg; 126 extern u_int utmp_len; 127 128 /* so we don't silently change behaviour */ 129 #ifdef USE_POSIX_THREADS 130 # error "USE_POSIX_THREADS replaced by UNSUPPORTED_POSIX_THREADS_HACK" 131 #endif 132 133 /* 134 * Formerly known as USE_POSIX_THREADS, using this is completely unsupported 135 * and generally a bad idea. Use at own risk and do not expect support if 136 * this breaks. 137 */ 138 #ifdef UNSUPPORTED_POSIX_THREADS_HACK 139 #error "foo" 140 #include <pthread.h> 141 /* 142 * Avoid namespace clash when *not* using pthreads for systems *with* 143 * pthreads, which unconditionally define pthread_t via sys/types.h 144 * (e.g. Linux) 145 */ 146 typedef pthread_t sp_pthread_t; 147 #else 148 typedef pid_t sp_pthread_t; 149 #define pthread_exit fake_pthread_exit 150 #define pthread_create fake_pthread_create 151 #define pthread_cancel fake_pthread_cancel 152 #define pthread_join fake_pthread_join 153 #endif 154 155 struct pam_ctxt { 156 sp_pthread_t pam_thread; 157 int pam_psock; 158 int pam_csock; 159 int pam_done; 160 }; 161 162 static void sshpam_free_ctx(void *); 163 static struct pam_ctxt *cleanup_ctxt; 164 165 #ifndef UNSUPPORTED_POSIX_THREADS_HACK 166 /* 167 * Simulate threads with processes. 168 */ 169 170 static int sshpam_thread_status = -1; 171 static sshsig_t sshpam_oldsig; 172 173 static void 174 sshpam_sigchld_handler(int sig) 175 { 176 ssh_signal(SIGCHLD, SIG_DFL); 177 if (cleanup_ctxt == NULL) 178 return; /* handler called after PAM cleanup, shouldn't happen */ 179 if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG) 180 <= 0) { 181 /* PAM thread has not exitted, privsep slave must have */ 182 kill(cleanup_ctxt->pam_thread, SIGTERM); 183 while (waitpid(cleanup_ctxt->pam_thread, 184 &sshpam_thread_status, 0) == -1) { 185 if (errno == EINTR) 186 continue; 187 return; 188 } 189 } 190 if (sshpam_thread_status == -1) 191 return; 192 if (WIFSIGNALED(sshpam_thread_status)) { 193 if (signal_is_crash(WTERMSIG(sshpam_thread_status))) 194 _exit(EXIT_CHILD_CRASH); 195 } else if (!WIFEXITED(sshpam_thread_status)) 196 _exit(EXIT_CHILD_CRASH); 197 198 } 199 200 /* ARGSUSED */ 201 __dead static void 202 pthread_exit(void *value) 203 { 204 _exit(0); 205 } 206 207 /* ARGSUSED */ 208 static int 209 pthread_create(sp_pthread_t *thread, const void *attr, 210 void *(*thread_start)(void *), void *arg) 211 { 212 pid_t pid; 213 struct pam_ctxt *ctx = arg; 214 215 sshpam_thread_status = -1; 216 switch ((pid = fork())) { 217 case -1: 218 error("fork(): %s", strerror(errno)); 219 return errno; 220 case 0: 221 close(ctx->pam_psock); 222 ctx->pam_psock = -1; 223 thread_start(arg); 224 _exit(1); 225 default: 226 *thread = pid; 227 close(ctx->pam_csock); 228 ctx->pam_csock = -1; 229 sshpam_oldsig = ssh_signal(SIGCHLD, sshpam_sigchld_handler); 230 return (0); 231 } 232 } 233 234 static int 235 pthread_cancel(sp_pthread_t thread) 236 { 237 ssh_signal(SIGCHLD, sshpam_oldsig); 238 return (kill(thread, SIGTERM)); 239 } 240 241 /* ARGSUSED */ 242 static int 243 pthread_join(sp_pthread_t thread, void **value) 244 { 245 int status; 246 247 if (sshpam_thread_status != -1) 248 return (sshpam_thread_status); 249 ssh_signal(SIGCHLD, sshpam_oldsig); 250 while (waitpid(thread, &status, 0) == -1) { 251 if (errno == EINTR) 252 continue; 253 fatal("%s: waitpid: %s", __func__, strerror(errno)); 254 } 255 return (status); 256 } 257 #endif 258 259 260 static pam_handle_t *sshpam_handle = NULL; 261 static int sshpam_err = 0; 262 static int sshpam_authenticated = 0; 263 static int sshpam_session_open = 0; 264 static int sshpam_cred_established = 0; 265 static int sshpam_account_status = -1; 266 static int sshpam_maxtries_reached = 0; 267 static char **sshpam_env = NULL; 268 static Authctxt *sshpam_authctxt = NULL; 269 static const char *sshpam_password = NULL; 270 static char *sshpam_rhost = NULL; 271 static char *sshpam_laddr = NULL; 272 273 /* Some PAM implementations don't implement this */ 274 #ifndef HAVE_PAM_GETENVLIST 275 static char ** 276 pam_getenvlist(pam_handle_t *pamh) 277 { 278 /* 279 * XXX - If necessary, we can still support environment passing 280 * for platforms without pam_getenvlist by searching for known 281 * env vars (e.g. KRB5CCNAME) from the PAM environment. 282 */ 283 return NULL; 284 } 285 #endif 286 287 #ifndef HAVE_PAM_PUTENV 288 static int 289 pam_putenv(pam_handle_t *pamh, const char *name_value) 290 { 291 return PAM_SUCCESS; 292 } 293 #endif /* HAVE_PAM_PUTENV */ 294 295 /* 296 * Some platforms, notably Solaris, do not enforce password complexity 297 * rules during pam_chauthtok() if the real uid of the calling process 298 * is 0, on the assumption that it's being called by "passwd" run by root. 299 * This wraps pam_chauthtok and sets/restore the real uid so PAM will do 300 * the right thing. 301 */ 302 #ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID 303 static int 304 sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags) 305 { 306 int result; 307 308 if (sshpam_authctxt == NULL) 309 fatal("PAM: sshpam_authctxt not initialized"); 310 if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1) 311 fatal("%s: setreuid failed: %s", __func__, strerror(errno)); 312 result = pam_chauthtok(pamh, flags); 313 if (setreuid(0, -1) == -1) 314 fatal("%s: setreuid failed: %s", __func__, strerror(errno)); 315 return result; 316 } 317 # define pam_chauthtok(a,b) (sshpam_chauthtok_ruid((a), (b))) 318 #endif 319 320 static void 321 sshpam_password_change_required(int reqd) 322 { 323 extern struct sshauthopt *auth_opts; 324 static int saved_port, saved_agent, saved_x11; 325 326 debug3("%s %d", __func__, reqd); 327 if (sshpam_authctxt == NULL) 328 fatal("%s: PAM authctxt not initialized", __func__); 329 sshpam_authctxt->force_pwchange = reqd; 330 if (reqd) { 331 saved_port = auth_opts->permit_port_forwarding_flag; 332 saved_agent = auth_opts->permit_agent_forwarding_flag; 333 saved_x11 = auth_opts->permit_x11_forwarding_flag; 334 auth_opts->permit_port_forwarding_flag = 0; 335 auth_opts->permit_agent_forwarding_flag = 0; 336 auth_opts->permit_x11_forwarding_flag = 0; 337 } else { 338 if (saved_port) 339 auth_opts->permit_port_forwarding_flag = saved_port; 340 if (saved_agent) 341 auth_opts->permit_agent_forwarding_flag = saved_agent; 342 if (saved_x11) 343 auth_opts->permit_x11_forwarding_flag = saved_x11; 344 } 345 } 346 347 /* Import regular and PAM environment from subprocess */ 348 static void 349 import_environments(struct sshbuf *b) 350 { 351 char *env; 352 u_int n, i, num_env; 353 int r; 354 355 debug3("PAM: %s entering", __func__); 356 357 #ifndef UNSUPPORTED_POSIX_THREADS_HACK 358 /* Import variables set by do_pam_account */ 359 if ((r = sshbuf_get_u32(b, &n)) != 0) 360 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 361 if (n > INT_MAX) 362 fatal("%s: invalid PAM account status %u", __func__, n); 363 sshpam_account_status = (int)n; 364 if ((r = sshbuf_get_u32(b, &n)) != 0) 365 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 366 sshpam_password_change_required(n != 0); 367 368 /* Import environment from subprocess */ 369 if ((r = sshbuf_get_u32(b, &num_env)) != 0) 370 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 371 if (num_env > 1024) { 372 fatal_f("received %u environment variables, expected <= 1024", 373 num_env); 374 } 375 sshpam_env = xcalloc(num_env + 1, sizeof(*sshpam_env)); 376 debug3("PAM: num env strings %u", num_env); 377 for(i = 0; i < num_env; i++) { 378 if ((r = sshbuf_get_cstring(b, &(sshpam_env[i]), NULL)) != 0) 379 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 380 } 381 sshpam_env[num_env] = NULL; 382 383 /* Import PAM environment from subprocess */ 384 if ((r = sshbuf_get_u32(b, &num_env)) != 0) 385 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 386 if (num_env > 1024) { 387 fatal_f("received %u PAM env variables, expected <= 1024", 388 num_env); 389 } 390 debug("PAM: num PAM env strings %u", num_env); 391 for (i = 0; i < num_env; i++) { 392 if ((r = sshbuf_get_cstring(b, &env, NULL)) != 0) 393 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 394 #ifdef HAVE_PAM_PUTENV 395 /* Errors are not fatal here */ 396 if ((r = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) { 397 error("PAM: pam_putenv: %s", 398 pam_strerror(sshpam_handle, r)); 399 } 400 #endif 401 /* 402 * XXX this possibly leaks env because it is not documented 403 * what pam_putenv() does with it. Does it copy it? Does it 404 * take ownweship? We don't know, so it's safest just to leak. 405 */ 406 } 407 #endif 408 } 409 410 /* 411 * Conversation function for authentication thread. 412 */ 413 static int 414 sshpam_thread_conv(int n, sshpam_const struct pam_message **msg, 415 struct pam_response **resp, void *data) 416 { 417 struct sshbuf *buffer; 418 struct pam_ctxt *ctxt; 419 struct pam_response *reply; 420 int r, i; 421 u_char status; 422 423 debug3("PAM: %s entering, %d messages", __func__, n); 424 *resp = NULL; 425 426 if (data == NULL) { 427 error("PAM: conversation function passed a null context"); 428 return (PAM_CONV_ERR); 429 } 430 ctxt = data; 431 if (n <= 0 || n > PAM_MAX_NUM_MSG) 432 return (PAM_CONV_ERR); 433 434 if ((reply = calloc(n, sizeof(*reply))) == NULL) 435 return PAM_CONV_ERR; 436 if ((buffer = sshbuf_new()) == NULL) { 437 free(reply); 438 return PAM_CONV_ERR; 439 } 440 441 for (i = 0; i < n; ++i) { 442 switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 443 case PAM_PROMPT_ECHO_OFF: 444 case PAM_PROMPT_ECHO_ON: 445 if ((r = sshbuf_put_cstring(buffer, 446 PAM_MSG_MEMBER(msg, i, msg))) != 0) 447 fatal("%s: buffer error: %s", 448 __func__, ssh_err(r)); 449 if (ssh_msg_send(ctxt->pam_csock, 450 PAM_MSG_MEMBER(msg, i, msg_style), buffer) == -1) 451 goto fail; 452 453 if (ssh_msg_recv(ctxt->pam_csock, buffer) == -1) 454 goto fail; 455 if ((r = sshbuf_get_u8(buffer, &status)) != 0) 456 fatal("%s: buffer error: %s", 457 __func__, ssh_err(r)); 458 if (status != PAM_AUTHTOK) 459 goto fail; 460 if ((r = sshbuf_get_cstring(buffer, 461 &reply[i].resp, NULL)) != 0) 462 fatal("%s: buffer error: %s", 463 __func__, ssh_err(r)); 464 break; 465 case PAM_ERROR_MSG: 466 case PAM_TEXT_INFO: 467 if ((r = sshbuf_put_cstring(buffer, 468 PAM_MSG_MEMBER(msg, i, msg))) != 0) 469 fatal("%s: buffer error: %s", 470 __func__, ssh_err(r)); 471 if (ssh_msg_send(ctxt->pam_csock, 472 PAM_MSG_MEMBER(msg, i, msg_style), buffer) == -1) 473 goto fail; 474 break; 475 default: 476 goto fail; 477 } 478 sshbuf_reset(buffer); 479 } 480 sshbuf_free(buffer); 481 *resp = reply; 482 return (PAM_SUCCESS); 483 484 fail: 485 for(i = 0; i < n; i++) { 486 free(reply[i].resp); 487 } 488 free(reply); 489 sshbuf_free(buffer); 490 return (PAM_CONV_ERR); 491 } 492 493 /* 494 * Authentication thread. 495 */ 496 static void * 497 sshpam_thread(void *ctxtp) 498 { 499 struct pam_ctxt *ctxt = ctxtp; 500 struct sshbuf *buffer = NULL; 501 struct pam_conv sshpam_conv; 502 int r, flags = (options.permit_empty_passwd == 0 ? 503 PAM_DISALLOW_NULL_AUTHTOK : 0); 504 #ifndef UNSUPPORTED_POSIX_THREADS_HACK 505 extern char **environ; 506 char **env_from_pam; 507 u_int i; 508 const char *pam_user; 509 const char **ptr_pam_user = &pam_user; 510 char *tz = getenv("TZ"); 511 512 sshpam_err = pam_get_item(sshpam_handle, PAM_USER, 513 (sshpam_const void **)ptr_pam_user); 514 if (sshpam_err != PAM_SUCCESS) 515 goto auth_fail; 516 517 environ[0] = NULL; 518 if (tz != NULL) 519 if (setenv("TZ", tz, 1) == -1) 520 error("PAM: could not set TZ environment: %s", 521 strerror(errno)); 522 523 if (sshpam_authctxt != NULL) { 524 setproctitle("%s [pam]", 525 sshpam_authctxt->valid ? pam_user : "unknown"); 526 } 527 #endif 528 529 sshpam_conv.conv = sshpam_thread_conv; 530 sshpam_conv.appdata_ptr = ctxt; 531 532 if (sshpam_authctxt == NULL) 533 fatal("%s: PAM authctxt not initialized", __func__); 534 535 if ((buffer = sshbuf_new()) == NULL) 536 fatal("%s: sshbuf_new failed", __func__); 537 538 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 539 (const void *)&sshpam_conv); 540 if (sshpam_err != PAM_SUCCESS) 541 goto auth_fail; 542 sshpam_err = pam_authenticate(sshpam_handle, flags); 543 if (sshpam_err == PAM_MAXTRIES) 544 sshpam_set_maxtries_reached(1); 545 if (sshpam_err != PAM_SUCCESS) 546 goto auth_fail; 547 548 if (!do_pam_account()) { 549 sshpam_err = PAM_ACCT_EXPIRED; 550 goto auth_fail; 551 } 552 if (sshpam_authctxt->force_pwchange) { 553 sshpam_err = pam_chauthtok(sshpam_handle, 554 PAM_CHANGE_EXPIRED_AUTHTOK); 555 if (sshpam_err != PAM_SUCCESS) 556 goto auth_fail; 557 sshpam_password_change_required(0); 558 } 559 560 if ((r = sshbuf_put_cstring(buffer, "OK")) != 0) 561 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 562 563 #ifndef UNSUPPORTED_POSIX_THREADS_HACK 564 /* Export variables set by do_pam_account */ 565 if ((r = sshbuf_put_u32(buffer, sshpam_account_status)) != 0 || 566 (r = sshbuf_put_u32(buffer, sshpam_authctxt->force_pwchange)) != 0) 567 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 568 569 /* Export any environment strings set in child */ 570 for (i = 0; environ[i] != NULL; i++) { 571 /* Count */ 572 if (i > INT_MAX) 573 fatal("%s: too many environment strings", __func__); 574 } 575 if ((r = sshbuf_put_u32(buffer, i)) != 0) 576 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 577 for (i = 0; environ[i] != NULL; i++) { 578 if ((r = sshbuf_put_cstring(buffer, environ[i])) != 0) 579 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 580 } 581 /* Export any environment strings set by PAM in child */ 582 env_from_pam = pam_getenvlist(sshpam_handle); 583 for (i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) { 584 /* Count */ 585 if (i > INT_MAX) 586 fatal("%s: too many PAM environment strings", __func__); 587 } 588 if ((r = sshbuf_put_u32(buffer, i)) != 0) 589 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 590 for (i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) { 591 if ((r = sshbuf_put_cstring(buffer, env_from_pam[i])) != 0) 592 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 593 } 594 #endif /* UNSUPPORTED_POSIX_THREADS_HACK */ 595 596 /* XXX - can't do much about an error here */ 597 ssh_msg_send(ctxt->pam_csock, sshpam_err, buffer); 598 sshbuf_free(buffer); 599 pthread_exit(NULL); 600 601 auth_fail: 602 if ((r = sshbuf_put_cstring(buffer, 603 pam_strerror(sshpam_handle, sshpam_err))) != 0) 604 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 605 /* XXX - can't do much about an error here */ 606 if (sshpam_err == PAM_ACCT_EXPIRED) 607 ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, buffer); 608 else if (sshpam_maxtries_reached) 609 ssh_msg_send(ctxt->pam_csock, PAM_MAXTRIES, buffer); 610 else 611 ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, buffer); 612 sshbuf_free(buffer); 613 pthread_exit(NULL); 614 615 return (NULL); /* Avoid warning for non-pthread case */ 616 } 617 618 void 619 sshpam_thread_cleanup(void) 620 { 621 struct pam_ctxt *ctxt = cleanup_ctxt; 622 623 debug3("PAM: %s entering", __func__); 624 if (ctxt != NULL && ctxt->pam_thread != 0) { 625 pthread_cancel(ctxt->pam_thread); 626 pthread_join(ctxt->pam_thread, NULL); 627 close(ctxt->pam_psock); 628 close(ctxt->pam_csock); 629 memset(ctxt, 0, sizeof(*ctxt)); 630 cleanup_ctxt = NULL; 631 } 632 } 633 634 static int 635 sshpam_null_conv(int n, sshpam_const struct pam_message **msg, 636 struct pam_response **resp, void *data) 637 { 638 debug3("PAM: %s entering, %d messages", __func__, n); 639 return (PAM_CONV_ERR); 640 } 641 642 static struct pam_conv null_conv = { sshpam_null_conv, NULL }; 643 644 static int 645 sshpam_store_conv(int n, sshpam_const struct pam_message **msg, 646 struct pam_response **resp, void *data) 647 { 648 struct pam_response *reply; 649 int r, i; 650 651 debug3("PAM: %s called with %d messages", __func__, n); 652 *resp = NULL; 653 654 if (n <= 0 || n > PAM_MAX_NUM_MSG) 655 return (PAM_CONV_ERR); 656 657 if ((reply = calloc(n, sizeof(*reply))) == NULL) 658 return (PAM_CONV_ERR); 659 660 for (i = 0; i < n; ++i) { 661 switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 662 case PAM_ERROR_MSG: 663 case PAM_TEXT_INFO: 664 if ((r = sshbuf_putf(loginmsg, "%s\n", 665 PAM_MSG_MEMBER(msg, i, msg))) != 0) 666 fatal("%s: buffer error: %s", 667 __func__, ssh_err(r)); 668 reply[i].resp_retcode = PAM_SUCCESS; 669 break; 670 default: 671 goto fail; 672 } 673 } 674 *resp = reply; 675 return (PAM_SUCCESS); 676 677 fail: 678 for(i = 0; i < n; i++) { 679 free(reply[i].resp); 680 } 681 free(reply); 682 return (PAM_CONV_ERR); 683 } 684 685 static struct pam_conv store_conv = { sshpam_store_conv, NULL }; 686 687 void 688 sshpam_cleanup(void) 689 { 690 if (sshpam_handle == NULL || !mm_is_monitor()) 691 return; 692 debug("PAM: cleanup"); 693 pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv); 694 if (sshpam_session_open) { 695 debug("PAM: closing session"); 696 pam_close_session(sshpam_handle, PAM_SILENT); 697 sshpam_session_open = 0; 698 } 699 if (sshpam_cred_established) { 700 debug("PAM: deleting credentials"); 701 pam_setcred(sshpam_handle, PAM_DELETE_CRED); 702 sshpam_cred_established = 0; 703 } 704 sshpam_authenticated = 0; 705 pam_end(sshpam_handle, sshpam_err); 706 sshpam_handle = NULL; 707 } 708 709 static int 710 sshpam_init(struct ssh *ssh, Authctxt *authctxt) 711 { 712 const char *pam_user, *user = authctxt->user; 713 const char **ptr_pam_user = &pam_user; 714 int r; 715 716 if (options.pam_service_name == NULL) 717 fatal_f("internal error: NULL PAM service name"); 718 #if defined(PAM_SUN_CODEBASE) && defined(PAM_MAX_RESP_SIZE) 719 /* Protect buggy PAM implementations from excessively long usernames */ 720 if (strlen(user) >= PAM_MAX_RESP_SIZE) 721 fatal("Username too long from %s port %d", 722 ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); 723 #endif 724 if (sshpam_handle == NULL) { 725 if (ssh == NULL) { 726 fatal("%s: called initially with no " 727 "packet context", __func__); 728 } 729 } 730 if (sshpam_handle != NULL) { 731 /* We already have a PAM context; check if the user matches */ 732 sshpam_err = pam_get_item(sshpam_handle, 733 PAM_USER, (sshpam_const void **)ptr_pam_user); 734 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0) 735 return (0); 736 pam_end(sshpam_handle, sshpam_err); 737 sshpam_handle = NULL; 738 } 739 debug("PAM: initializing for \"%s\" with service \"%s\"", user, 740 options.pam_service_name); 741 sshpam_err = pam_start(options.pam_service_name, user, 742 &store_conv, &sshpam_handle); 743 sshpam_authctxt = authctxt; 744 745 if (sshpam_err != PAM_SUCCESS) { 746 pam_end(sshpam_handle, sshpam_err); 747 sshpam_handle = NULL; 748 return (-1); 749 } 750 751 if (ssh != NULL && sshpam_rhost == NULL) { 752 /* 753 * We need to cache these as we don't have packet context 754 * during the kbdint flow. 755 */ 756 sshpam_rhost = xstrdup(auth_get_canonical_hostname(ssh, 757 options.use_dns)); 758 sshpam_laddr = get_local_ipaddr( 759 ssh_packet_get_connection_in(ssh)); 760 } 761 if (sshpam_rhost != NULL) { 762 debug("PAM: setting PAM_RHOST to \"%s\"", sshpam_rhost); 763 sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, 764 sshpam_rhost); 765 if (sshpam_err != PAM_SUCCESS) { 766 pam_end(sshpam_handle, sshpam_err); 767 sshpam_handle = NULL; 768 return (-1); 769 } 770 } 771 if (ssh != NULL && sshpam_laddr != NULL) { 772 char *conninfo; 773 774 /* Put SSH_CONNECTION in the PAM environment too */ 775 xasprintf(&conninfo, "SSH_CONNECTION=%.50s %d %.50s %d", 776 ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), 777 sshpam_laddr, ssh_local_port(ssh)); 778 if ((r = pam_putenv(sshpam_handle, conninfo)) != PAM_SUCCESS) 779 logit("pam_putenv: %s", pam_strerror(sshpam_handle, r)); 780 free(conninfo); 781 } 782 783 #ifdef PAM_TTY_KLUDGE 784 /* 785 * Some silly PAM modules (e.g. pam_time) require a TTY to operate. 786 * sshd doesn't set the tty until too late in the auth process and 787 * may not even set one (for tty-less connections) 788 */ 789 debug("PAM: setting PAM_TTY to \"ssh\""); 790 sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh"); 791 if (sshpam_err != PAM_SUCCESS) { 792 pam_end(sshpam_handle, sshpam_err); 793 sshpam_handle = NULL; 794 return (-1); 795 } 796 #endif 797 return (0); 798 } 799 800 static void 801 expose_authinfo(const char *caller) 802 { 803 char *auth_info; 804 805 /* 806 * Expose authentication information to PAM. 807 * The environment variable is versioned. Please increment the 808 * version suffix if the format of session_info changes. 809 */ 810 if (sshpam_authctxt->session_info == NULL) 811 auth_info = xstrdup(""); 812 else if ((auth_info = sshbuf_dup_string( 813 sshpam_authctxt->session_info)) == NULL) 814 fatal("%s: sshbuf_dup_string failed", __func__); 815 816 debug2("%s: auth information in SSH_AUTH_INFO_0", caller); 817 do_pam_putenv("SSH_AUTH_INFO_0", auth_info); 818 free(auth_info); 819 } 820 821 static void * 822 sshpam_init_ctx(Authctxt *authctxt) 823 { 824 struct pam_ctxt *ctxt; 825 int result, socks[2]; 826 827 debug3("PAM: %s entering", __func__); 828 /* 829 * Refuse to start if we don't have PAM enabled or do_pam_account 830 * has previously failed. 831 */ 832 if (!options.use_pam || sshpam_account_status == 0) 833 return NULL; 834 835 /* Initialize PAM */ 836 if (sshpam_init(NULL, authctxt) == -1) { 837 error("PAM: initialization failed"); 838 return (NULL); 839 } 840 841 expose_authinfo(__func__); 842 ctxt = xcalloc(1, sizeof *ctxt); 843 844 /* Start the authentication thread */ 845 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) { 846 error("PAM: failed create sockets: %s", strerror(errno)); 847 free(ctxt); 848 return (NULL); 849 } 850 ctxt->pam_psock = socks[0]; 851 ctxt->pam_csock = socks[1]; 852 result = pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt); 853 if (result != 0) { 854 error("PAM: failed to start authentication thread: %s", 855 strerror(result)); 856 close(socks[0]); 857 close(socks[1]); 858 free(ctxt); 859 return (NULL); 860 } 861 cleanup_ctxt = ctxt; 862 return (ctxt); 863 } 864 865 static int 866 sshpam_query(void *ctx, char **name, char **info, 867 u_int *num, char ***prompts, u_int **echo_on) 868 { 869 struct sshbuf *buffer; 870 struct pam_ctxt *ctxt = ctx; 871 size_t plen; 872 u_char type; 873 char *msg; 874 size_t len, mlen, nmesg = 0; 875 int r; 876 877 debug3("PAM: %s entering", __func__); 878 if ((buffer = sshbuf_new()) == NULL) 879 fatal("%s: sshbuf_new failed", __func__); 880 *name = xstrdup(""); 881 *info = xstrdup(""); 882 *prompts = xmalloc(sizeof(char *)); 883 **prompts = NULL; 884 plen = 0; 885 *echo_on = xmalloc(sizeof(u_int)); 886 while (ssh_msg_recv(ctxt->pam_psock, buffer) == 0) { 887 if (++nmesg > PAM_MAX_NUM_MSG) 888 fatal_f("too many query messages"); 889 if ((r = sshbuf_get_u8(buffer, &type)) != 0 || 890 (r = sshbuf_get_cstring(buffer, &msg, &mlen)) != 0) 891 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 892 switch (type) { 893 case PAM_PROMPT_ECHO_ON: 894 case PAM_PROMPT_ECHO_OFF: 895 *num = 1; 896 len = plen + mlen + 1; 897 **prompts = xreallocarray(**prompts, 1, len); 898 strlcpy(**prompts + plen, msg, len - plen); 899 plen += mlen; 900 **echo_on = (type == PAM_PROMPT_ECHO_ON); 901 free(msg); 902 sshbuf_free(buffer); 903 return (0); 904 case PAM_ERROR_MSG: 905 case PAM_TEXT_INFO: 906 /* accumulate messages */ 907 len = plen + mlen + 2; 908 **prompts = xreallocarray(**prompts, 1, len); 909 strlcpy(**prompts + plen, msg, len - plen); 910 plen += mlen; 911 strlcat(**prompts + plen, "\n", len - plen); 912 plen++; 913 free(msg); 914 break; 915 case PAM_ACCT_EXPIRED: 916 case PAM_MAXTRIES: 917 if (type == PAM_ACCT_EXPIRED) 918 sshpam_account_status = 0; 919 if (type == PAM_MAXTRIES) 920 sshpam_set_maxtries_reached(1); 921 /* FALLTHROUGH */ 922 case PAM_AUTH_ERR: 923 debug3("PAM: %s", pam_strerror(sshpam_handle, type)); 924 if (**prompts != NULL && strlen(**prompts) != 0) { 925 free(*info); 926 *info = **prompts; 927 **prompts = NULL; 928 *num = 0; 929 **echo_on = 0; 930 ctxt->pam_done = -1; 931 free(msg); 932 sshbuf_free(buffer); 933 return 0; 934 } 935 /* FALLTHROUGH */ 936 case PAM_SUCCESS: 937 if (**prompts != NULL) { 938 /* drain any accumulated messages */ 939 debug("PAM: %s", **prompts); 940 if ((r = sshbuf_put(loginmsg, **prompts, 941 strlen(**prompts))) != 0) 942 fatal("%s: buffer error: %s", 943 __func__, ssh_err(r)); 944 free(**prompts); 945 **prompts = NULL; 946 } 947 if (type == PAM_SUCCESS) { 948 if (!sshpam_authctxt->valid || 949 (sshpam_authctxt->pw->pw_uid == 0 && 950 options.permit_root_login != PERMIT_YES)) 951 fatal("Internal error: PAM auth " 952 "succeeded when it should have " 953 "failed"); 954 import_environments(buffer); 955 *num = 0; 956 **echo_on = 0; 957 ctxt->pam_done = 1; 958 free(msg); 959 sshbuf_free(buffer); 960 return (0); 961 } 962 pfilter_notify(1); 963 error("PAM: %s for %s%.100s from %.100s", msg, 964 sshpam_authctxt->valid ? "" : "illegal user ", 965 sshpam_authctxt->user, sshpam_rhost); 966 /* FALLTHROUGH */ 967 default: 968 *num = 0; 969 **echo_on = 0; 970 free(msg); 971 ctxt->pam_done = -1; 972 sshbuf_free(buffer); 973 return (-1); 974 } 975 } 976 sshbuf_free(buffer); 977 return (-1); 978 } 979 980 /* 981 * Returns a junk password of identical length to that the user supplied. 982 * Used to mitigate timing attacks against crypt(3)/PAM stacks that 983 * vary processing time in proportion to password length. 984 */ 985 static char * 986 fake_password(const char *wire_password) 987 { 988 const char junk[] = "\b\n\r\177INCORRECT"; 989 char *ret = NULL; 990 size_t i, l = wire_password != NULL ? strlen(wire_password) : 0; 991 992 if (l >= INT_MAX) 993 fatal("%s: password length too long: %zu", __func__, l); 994 995 ret = malloc(l + 1); 996 if (ret == NULL) 997 return NULL; 998 for (i = 0; i < l; i++) 999 ret[i] = junk[i % (sizeof(junk) - 1)]; 1000 ret[i] = '\0'; 1001 return ret; 1002 } 1003 1004 /* XXX - see also comment in auth-chall.c:verify_response */ 1005 static int 1006 sshpam_respond(void *ctx, u_int num, char **resp) 1007 { 1008 struct sshbuf *buffer; 1009 struct pam_ctxt *ctxt = ctx; 1010 char *fake; 1011 int r; 1012 1013 debug2("PAM: %s entering, %u responses", __func__, num); 1014 switch (ctxt->pam_done) { 1015 case 1: 1016 sshpam_authenticated = 1; 1017 return (0); 1018 case 0: 1019 break; 1020 default: 1021 return (-1); 1022 } 1023 if (num != 1) { 1024 error("PAM: expected one response, got %u", num); 1025 return (-1); 1026 } 1027 if ((buffer = sshbuf_new()) == NULL) 1028 fatal("%s: sshbuf_new failed", __func__); 1029 if (sshpam_authctxt->valid && 1030 (sshpam_authctxt->pw->pw_uid != 0 || 1031 options.permit_root_login == PERMIT_YES)) { 1032 if ((r = sshbuf_put_cstring(buffer, *resp)) != 0) 1033 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1034 } else { 1035 fake = fake_password(*resp); 1036 if ((r = sshbuf_put_cstring(buffer, fake)) != 0) 1037 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1038 free(fake); 1039 } 1040 if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, buffer) == -1) { 1041 sshbuf_free(buffer); 1042 return (-1); 1043 } 1044 sshbuf_free(buffer); 1045 return (1); 1046 } 1047 1048 static void 1049 sshpam_free_ctx(void *ctxtp) 1050 { 1051 struct pam_ctxt *ctxt = ctxtp; 1052 1053 debug3("PAM: %s entering", __func__); 1054 sshpam_thread_cleanup(); 1055 free(ctxt); 1056 /* 1057 * We don't call sshpam_cleanup() here because we may need the PAM 1058 * handle at a later stage, e.g. when setting up a session. It's 1059 * still on the cleanup list, so pam_end() *will* be called before 1060 * the server process terminates. 1061 */ 1062 } 1063 1064 KbdintDevice sshpam_device = { 1065 "pam", 1066 sshpam_init_ctx, 1067 sshpam_query, 1068 sshpam_respond, 1069 sshpam_free_ctx 1070 }; 1071 1072 KbdintDevice mm_sshpam_device = { 1073 "pam", 1074 mm_sshpam_init_ctx, 1075 mm_sshpam_query, 1076 mm_sshpam_respond, 1077 mm_sshpam_free_ctx 1078 }; 1079 1080 /* 1081 * This replaces auth-pam.c 1082 */ 1083 void 1084 start_pam(struct ssh *ssh) 1085 { 1086 Authctxt *authctxt = (Authctxt *)ssh->authctxt; 1087 1088 if (!options.use_pam) 1089 fatal("PAM: initialisation requested when UsePAM=no"); 1090 1091 if (sshpam_init(ssh, authctxt) == -1) 1092 fatal("PAM: initialisation failed"); 1093 } 1094 1095 void 1096 finish_pam(void) 1097 { 1098 sshpam_cleanup(); 1099 } 1100 1101 1102 u_int 1103 do_pam_account(void) 1104 { 1105 debug("%s: called", __func__); 1106 if (sshpam_account_status != -1) 1107 return (sshpam_account_status); 1108 1109 expose_authinfo(__func__); 1110 1111 sshpam_err = pam_acct_mgmt(sshpam_handle, 0); 1112 debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err, 1113 pam_strerror(sshpam_handle, sshpam_err)); 1114 1115 if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) { 1116 sshpam_account_status = 0; 1117 return (sshpam_account_status); 1118 } 1119 1120 if (sshpam_err == PAM_NEW_AUTHTOK_REQD) 1121 sshpam_password_change_required(1); 1122 1123 sshpam_account_status = 1; 1124 return (sshpam_account_status); 1125 } 1126 1127 void 1128 do_pam_setcred(void) 1129 { 1130 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 1131 (const void *)&store_conv); 1132 if (sshpam_err != PAM_SUCCESS) 1133 fatal("PAM: failed to set PAM_CONV: %s", 1134 pam_strerror(sshpam_handle, sshpam_err)); 1135 debug("PAM: establishing credentials"); 1136 sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED); 1137 if (sshpam_err == PAM_SUCCESS) { 1138 sshpam_cred_established = 1; 1139 return; 1140 } 1141 if (sshpam_authenticated) 1142 fatal("PAM: pam_setcred(): %s", 1143 pam_strerror(sshpam_handle, sshpam_err)); 1144 else 1145 debug("PAM: pam_setcred(): %s", 1146 pam_strerror(sshpam_handle, sshpam_err)); 1147 } 1148 1149 #if 0 1150 static int 1151 sshpam_tty_conv(int n, sshpam_const struct pam_message **msg, 1152 struct pam_response **resp, void *data) 1153 { 1154 char input[PAM_MAX_MSG_SIZE]; 1155 struct pam_response *reply; 1156 int i; 1157 1158 debug3("PAM: %s called with %d messages", __func__, n); 1159 1160 *resp = NULL; 1161 1162 if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO)) 1163 return (PAM_CONV_ERR); 1164 1165 if ((reply = calloc(n, sizeof(*reply))) == NULL) 1166 return (PAM_CONV_ERR); 1167 1168 for (i = 0; i < n; ++i) { 1169 switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 1170 case PAM_PROMPT_ECHO_OFF: 1171 reply[i].resp = 1172 read_passphrase(PAM_MSG_MEMBER(msg, i, msg), 1173 RP_ALLOW_STDIN); 1174 reply[i].resp_retcode = PAM_SUCCESS; 1175 break; 1176 case PAM_PROMPT_ECHO_ON: 1177 fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 1178 if (fgets(input, sizeof input, stdin) == NULL) 1179 input[0] = '\0'; 1180 if ((reply[i].resp = strdup(input)) == NULL) 1181 goto fail; 1182 reply[i].resp_retcode = PAM_SUCCESS; 1183 break; 1184 case PAM_ERROR_MSG: 1185 case PAM_TEXT_INFO: 1186 fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 1187 reply[i].resp_retcode = PAM_SUCCESS; 1188 break; 1189 default: 1190 goto fail; 1191 } 1192 } 1193 *resp = reply; 1194 return (PAM_SUCCESS); 1195 1196 fail: 1197 for(i = 0; i < n; i++) { 1198 free(reply[i].resp); 1199 } 1200 free(reply); 1201 return (PAM_CONV_ERR); 1202 } 1203 1204 static struct pam_conv tty_conv = { sshpam_tty_conv, NULL }; 1205 #endif 1206 1207 /* 1208 * XXX this should be done in the authentication phase, but ssh1 doesn't 1209 * support that 1210 */ 1211 __dead /* fatal is __dead */ 1212 void 1213 do_pam_chauthtok(void) 1214 { 1215 fatal("Password expired"); 1216 #if 0 1217 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 1218 (const void *)&tty_conv); 1219 if (sshpam_err != PAM_SUCCESS) 1220 fatal("PAM: failed to set PAM_CONV: %s", 1221 pam_strerror(sshpam_handle, sshpam_err)); 1222 debug("PAM: changing password"); 1223 sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK); 1224 if (sshpam_err != PAM_SUCCESS) 1225 fatal("PAM: pam_chauthtok(): %s", 1226 pam_strerror(sshpam_handle, sshpam_err)); 1227 #endif 1228 } 1229 1230 void 1231 do_pam_session(struct ssh *ssh) 1232 { 1233 debug3("PAM: opening session"); 1234 1235 expose_authinfo(__func__); 1236 1237 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 1238 (const void *)&store_conv); 1239 if (sshpam_err != PAM_SUCCESS) 1240 fatal("PAM: failed to set PAM_CONV: %s", 1241 pam_strerror(sshpam_handle, sshpam_err)); 1242 sshpam_err = pam_open_session(sshpam_handle, 0); 1243 if (sshpam_err == PAM_SUCCESS) 1244 sshpam_session_open = 1; 1245 else { 1246 sshpam_session_open = 0; 1247 auth_restrict_session(ssh); 1248 error("PAM: pam_open_session(): %s", 1249 pam_strerror(sshpam_handle, sshpam_err)); 1250 } 1251 1252 } 1253 1254 int 1255 is_pam_session_open(void) 1256 { 1257 return sshpam_session_open; 1258 } 1259 1260 /* 1261 * Set a PAM environment string. We need to do this so that the session 1262 * modules can handle things like Kerberos/GSI credentials that appear 1263 * during the ssh authentication process. 1264 */ 1265 int 1266 do_pam_putenv(const char *name, char *value) 1267 { 1268 int ret = 1; 1269 #ifdef HAVE_PAM_PUTENV 1270 char *compound; 1271 size_t len; 1272 1273 len = strlen(name) + strlen(value) + 2; 1274 compound = xmalloc(len); 1275 1276 snprintf(compound, len, "%s=%s", name, value); 1277 ret = pam_putenv(sshpam_handle, compound); 1278 free(compound); 1279 #endif 1280 1281 return (ret); 1282 } 1283 1284 char ** 1285 fetch_pam_child_environment(void) 1286 { 1287 return sshpam_env; 1288 } 1289 1290 char ** 1291 fetch_pam_environment(void) 1292 { 1293 return (pam_getenvlist(sshpam_handle)); 1294 } 1295 1296 void 1297 free_pam_environment(char **env) 1298 { 1299 char **envp; 1300 1301 if (env == NULL) 1302 return; 1303 1304 for (envp = env; *envp; envp++) 1305 free(*envp); 1306 free(env); 1307 } 1308 1309 /* 1310 * "Blind" conversation function for password authentication. Assumes that 1311 * echo-off prompts are for the password and stores messages for later 1312 * display. 1313 */ 1314 static int 1315 sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg, 1316 struct pam_response **resp, void *data) 1317 { 1318 struct pam_response *reply; 1319 int r, i; 1320 size_t len; 1321 1322 debug3("PAM: %s called with %d messages", __func__, n); 1323 1324 *resp = NULL; 1325 1326 if (n <= 0 || n > PAM_MAX_NUM_MSG) 1327 return (PAM_CONV_ERR); 1328 1329 if ((reply = calloc(n, sizeof(*reply))) == NULL) 1330 return (PAM_CONV_ERR); 1331 1332 for (i = 0; i < n; ++i) { 1333 switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 1334 case PAM_PROMPT_ECHO_OFF: 1335 if (sshpam_password == NULL) 1336 goto fail; 1337 if ((reply[i].resp = strdup(sshpam_password)) == NULL) 1338 goto fail; 1339 reply[i].resp_retcode = PAM_SUCCESS; 1340 break; 1341 case PAM_ERROR_MSG: 1342 case PAM_TEXT_INFO: 1343 len = strlen(PAM_MSG_MEMBER(msg, i, msg)); 1344 if (len > 0) { 1345 if ((r = sshbuf_putf(loginmsg, "%s\n", 1346 PAM_MSG_MEMBER(msg, i, msg))) != 0) 1347 fatal("%s: buffer error: %s", 1348 __func__, ssh_err(r)); 1349 } 1350 if ((reply[i].resp = strdup("")) == NULL) 1351 goto fail; 1352 reply[i].resp_retcode = PAM_SUCCESS; 1353 break; 1354 default: 1355 goto fail; 1356 } 1357 } 1358 *resp = reply; 1359 return (PAM_SUCCESS); 1360 1361 fail: 1362 for(i = 0; i < n; i++) { 1363 free(reply[i].resp); 1364 } 1365 free(reply); 1366 return (PAM_CONV_ERR); 1367 } 1368 1369 static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL }; 1370 1371 /* 1372 * Attempt password authentication via PAM 1373 */ 1374 int 1375 sshpam_auth_passwd(Authctxt *authctxt, const char *password) 1376 { 1377 int flags = (options.permit_empty_passwd == 0 ? 1378 PAM_DISALLOW_NULL_AUTHTOK : 0); 1379 char *fake = NULL; 1380 1381 if (!options.use_pam || sshpam_handle == NULL) 1382 fatal("PAM: %s called when PAM disabled or failed to " 1383 "initialise.", __func__); 1384 1385 sshpam_password = password; 1386 sshpam_authctxt = authctxt; 1387 1388 /* 1389 * If the user logging in is invalid, or is root but is not permitted 1390 * by PermitRootLogin, use an invalid password to prevent leaking 1391 * information via timing (eg if the PAM config has a delay on fail). 1392 */ 1393 if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && 1394 options.permit_root_login != PERMIT_YES)) 1395 sshpam_password = fake = fake_password(password); 1396 1397 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 1398 (const void *)&passwd_conv); 1399 if (sshpam_err != PAM_SUCCESS) 1400 fatal("PAM: %s: failed to set PAM_CONV: %s", __func__, 1401 pam_strerror(sshpam_handle, sshpam_err)); 1402 1403 sshpam_err = pam_authenticate(sshpam_handle, flags); 1404 sshpam_password = NULL; 1405 free(fake); 1406 if (sshpam_err == PAM_MAXTRIES) 1407 sshpam_set_maxtries_reached(1); 1408 if (sshpam_err == PAM_SUCCESS && authctxt->valid) { 1409 debug("PAM: password authentication accepted for %.100s", 1410 authctxt->user); 1411 return 1; 1412 } else { 1413 debug("PAM: password authentication failed for %.100s: %s", 1414 authctxt->valid ? authctxt->user : "an illegal user", 1415 pam_strerror(sshpam_handle, sshpam_err)); 1416 return 0; 1417 } 1418 } 1419 1420 int 1421 sshpam_get_maxtries_reached(void) 1422 { 1423 return sshpam_maxtries_reached; 1424 } 1425 1426 void 1427 sshpam_set_maxtries_reached(int reached) 1428 { 1429 if (reached == 0 || sshpam_maxtries_reached) 1430 return; 1431 sshpam_maxtries_reached = 1; 1432 options.password_authentication = 0; 1433 options.kbd_interactive_authentication = 0; 1434 } 1435 #endif /* USE_PAM */ 1436