]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - crypto/openssh/auth-pam.c
This commit was generated by cvs2svn to compensate for changes in r162852,
[FreeBSD/FreeBSD.git] / crypto / openssh / auth-pam.c
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  * Copyright (c) 2003,2004 Damien Miller <djm@mindrot.org>
33  * Copyright (c) 2003,2004 Darren Tucker <dtucker@zip.com.au>
34  *
35  * Permission to use, copy, modify, and distribute this software for any
36  * purpose with or without fee is hereby granted, provided that the above
37  * copyright notice and this permission notice appear in all copies.
38  *
39  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
40  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
41  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
42  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
43  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
44  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
45  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
46  */
47
48 /* Based on $xFreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */
49 #include "includes.h"
50 RCSID("$Id: auth-pam.c,v 1.128 2006/01/29 05:46:13 dtucker Exp $");
51 RCSID("$FreeBSD$");
52
53 #ifdef USE_PAM
54 #if defined(HAVE_SECURITY_PAM_APPL_H)
55 #include <security/pam_appl.h>
56 #elif defined (HAVE_PAM_PAM_APPL_H)
57 #include <pam/pam_appl.h>
58 #endif
59
60 /* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */
61 #ifdef PAM_SUN_CODEBASE
62 # define sshpam_const           /* Solaris, HP-UX, AIX */
63 #else
64 # define sshpam_const   const   /* LinuxPAM, OpenPAM */
65 #endif
66
67 #include "auth.h"
68 #include "auth-pam.h"
69 #include "buffer.h"
70 #include "bufaux.h"
71 #include "canohost.h"
72 #include "log.h"
73 #include "monitor_wrap.h"
74 #include "msg.h"
75 #include "packet.h"
76 #include "misc.h"
77 #include "servconf.h"
78 #include "ssh2.h"
79 #include "xmalloc.h"
80 #include "auth-options.h"
81
82 extern ServerOptions options;
83 extern Buffer loginmsg;
84 extern int compat20;
85 extern u_int utmp_len;
86
87 /* so we don't silently change behaviour */
88 #ifdef USE_POSIX_THREADS
89 # error "USE_POSIX_THREADS replaced by UNSUPPORTED_POSIX_THREADS_HACK"
90 #endif
91
92 /*
93  * Formerly known as USE_POSIX_THREADS, using this is completely unsupported
94  * and generally a bad idea.  Use at own risk and do not expect support if
95  * this breaks.
96  */
97 #ifdef UNSUPPORTED_POSIX_THREADS_HACK
98 #include <pthread.h>
99 /*
100  * Avoid namespace clash when *not* using pthreads for systems *with*
101  * pthreads, which unconditionally define pthread_t via sys/types.h
102  * (e.g. Linux)
103  */
104 typedef pthread_t sp_pthread_t;
105 #else
106 typedef pid_t sp_pthread_t;
107 #endif
108
109 struct pam_ctxt {
110         sp_pthread_t     pam_thread;
111         int              pam_psock;
112         int              pam_csock;
113         int              pam_done;
114 };
115
116 static void sshpam_free_ctx(void *);
117 static struct pam_ctxt *cleanup_ctxt;
118
119 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
120 /*
121  * Simulate threads with processes.
122  */
123
124 static int sshpam_thread_status = -1;
125 static mysig_t sshpam_oldsig;
126
127 static void
128 sshpam_sigchld_handler(int sig)
129 {
130         signal(SIGCHLD, SIG_DFL);
131         if (cleanup_ctxt == NULL)
132                 return; /* handler called after PAM cleanup, shouldn't happen */
133         if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG)
134             <= 0) {
135                 /* PAM thread has not exitted, privsep slave must have */
136                 kill(cleanup_ctxt->pam_thread, SIGTERM);
137                 if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0)
138                     <= 0)
139                         return; /* could not wait */
140         }
141         if (WIFSIGNALED(sshpam_thread_status) &&
142             WTERMSIG(sshpam_thread_status) == SIGTERM)
143                 return; /* terminated by pthread_cancel */
144         if (!WIFEXITED(sshpam_thread_status))
145                 fatal("PAM: authentication thread exited unexpectedly");
146         if (WEXITSTATUS(sshpam_thread_status) != 0)
147                 fatal("PAM: authentication thread exited uncleanly");
148 }
149
150 static void
151 pthread_exit(void *value __unused)
152 {
153         _exit(0);
154 }
155
156 static int
157 pthread_create(sp_pthread_t *thread, const void *attr __unused,
158     void *(*thread_start)(void *), void *arg)
159 {
160         pid_t pid;
161         struct pam_ctxt *ctx = arg;
162
163         sshpam_thread_status = -1;
164         switch ((pid = fork())) {
165         case -1:
166                 error("fork(): %s", strerror(errno));
167                 return (-1);
168         case 0:
169                 close(ctx->pam_psock);
170                 ctx->pam_psock = -1;
171                 thread_start(arg);
172                 _exit(1);
173         default:
174                 *thread = pid;
175                 close(ctx->pam_csock);
176                 ctx->pam_csock = -1;
177                 sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler);
178                 return (0);
179         }
180 }
181
182 static int
183 pthread_cancel(sp_pthread_t thread)
184 {
185         signal(SIGCHLD, sshpam_oldsig);
186         return (kill(thread, SIGTERM));
187 }
188
189 static int
190 pthread_join(sp_pthread_t thread, void **value __unused)
191 {
192         int status;
193
194         if (sshpam_thread_status != -1)
195                 return (sshpam_thread_status);
196         signal(SIGCHLD, sshpam_oldsig);
197         waitpid(thread, &status, 0);
198         return (status);
199 }
200 #endif
201
202
203 static pam_handle_t *sshpam_handle = NULL;
204 static int sshpam_err = 0;
205 static int sshpam_authenticated = 0;
206 static int sshpam_session_open = 0;
207 static int sshpam_cred_established = 0;
208 static int sshpam_account_status = -1;
209 static char **sshpam_env = NULL;
210 static Authctxt *sshpam_authctxt = NULL;
211 static const char *sshpam_password = NULL;
212 static char badpw[] = "\b\n\r\177INCORRECT";
213
214 /* Some PAM implementations don't implement this */
215 #ifndef HAVE_PAM_GETENVLIST
216 static char **
217 pam_getenvlist(pam_handle_t *pamh)
218 {
219         /*
220          * XXX - If necessary, we can still support envrionment passing
221          * for platforms without pam_getenvlist by searching for known
222          * env vars (e.g. KRB5CCNAME) from the PAM environment.
223          */
224          return NULL;
225 }
226 #endif
227
228 /*
229  * Some platforms, notably Solaris, do not enforce password complexity
230  * rules during pam_chauthtok() if the real uid of the calling process
231  * is 0, on the assumption that it's being called by "passwd" run by root.
232  * This wraps pam_chauthtok and sets/restore the real uid so PAM will do
233  * the right thing.
234  */
235 #ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID
236 static int
237 sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags)
238 {
239         int result;
240
241         if (sshpam_authctxt == NULL)
242                 fatal("PAM: sshpam_authctxt not initialized");
243         if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1)
244                 fatal("%s: setreuid failed: %s", __func__, strerror(errno));
245         result = pam_chauthtok(pamh, flags);
246         if (setreuid(0, -1) == -1)
247                 fatal("%s: setreuid failed: %s", __func__, strerror(errno));
248         return result;
249 }
250 # define pam_chauthtok(a,b)     (sshpam_chauthtok_ruid((a), (b)))
251 #endif
252
253 void
254 sshpam_password_change_required(int reqd)
255 {
256         debug3("%s %d", __func__, reqd);
257         if (sshpam_authctxt == NULL)
258                 fatal("%s: PAM authctxt not initialized", __func__);
259         sshpam_authctxt->force_pwchange = reqd;
260         if (reqd) {
261                 no_port_forwarding_flag |= 2;
262                 no_agent_forwarding_flag |= 2;
263                 no_x11_forwarding_flag |= 2;
264         } else {
265                 no_port_forwarding_flag &= ~2;
266                 no_agent_forwarding_flag &= ~2;
267                 no_x11_forwarding_flag &= ~2;
268         }
269 }
270
271 /* Import regular and PAM environment from subprocess */
272 static void
273 import_environments(Buffer *b)
274 {
275         char *env;
276         u_int i, num_env;
277         int err;
278
279         debug3("PAM: %s entering", __func__);
280
281 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
282         /* Import variables set by do_pam_account */
283         sshpam_account_status = buffer_get_int(b);
284         sshpam_password_change_required(buffer_get_int(b));
285
286         /* Import environment from subprocess */
287         num_env = buffer_get_int(b);
288         sshpam_env = xmalloc((num_env + 1) * sizeof(*sshpam_env));
289         debug3("PAM: num env strings %d", num_env);
290         for(i = 0; i < num_env; i++)
291                 sshpam_env[i] = buffer_get_string(b, NULL);
292
293         sshpam_env[num_env] = NULL;
294
295         /* Import PAM environment from subprocess */
296         num_env = buffer_get_int(b);
297         debug("PAM: num PAM env strings %d", num_env);
298         for(i = 0; i < num_env; i++) {
299                 env = buffer_get_string(b, NULL);
300
301 #ifdef HAVE_PAM_PUTENV
302                 /* Errors are not fatal here */
303                 if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) {
304                         error("PAM: pam_putenv: %s",
305                             pam_strerror(sshpam_handle, sshpam_err));
306                 }
307 #endif
308         }
309 #endif
310 }
311
312 /*
313  * Conversation function for authentication thread.
314  */
315 static int
316 sshpam_thread_conv(int n, sshpam_const struct pam_message **msg,
317     struct pam_response **resp, void *data)
318 {
319         Buffer buffer;
320         struct pam_ctxt *ctxt;
321         struct pam_response *reply;
322         int i;
323
324         debug3("PAM: %s entering, %d messages", __func__, n);
325         *resp = NULL;
326
327         if (data == NULL) {
328                 error("PAM: conversation function passed a null context");
329                 return (PAM_CONV_ERR);
330         }
331         ctxt = data;
332         if (n <= 0 || n > PAM_MAX_NUM_MSG)
333                 return (PAM_CONV_ERR);
334
335         if ((reply = malloc(n * sizeof(*reply))) == NULL)
336                 return (PAM_CONV_ERR);
337         memset(reply, 0, n * sizeof(*reply));
338
339         buffer_init(&buffer);
340         for (i = 0; i < n; ++i) {
341                 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
342                 case PAM_PROMPT_ECHO_OFF:
343                         buffer_put_cstring(&buffer,
344                             PAM_MSG_MEMBER(msg, i, msg));
345                         if (ssh_msg_send(ctxt->pam_csock,
346                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
347                                 goto fail;
348                         if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
349                                 goto fail;
350                         if (buffer_get_char(&buffer) != PAM_AUTHTOK)
351                                 goto fail;
352                         reply[i].resp = buffer_get_string(&buffer, NULL);
353                         break;
354                 case PAM_PROMPT_ECHO_ON:
355                         buffer_put_cstring(&buffer,
356                             PAM_MSG_MEMBER(msg, i, msg));
357                         if (ssh_msg_send(ctxt->pam_csock,
358                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
359                                 goto fail;
360                         if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
361                                 goto fail;
362                         if (buffer_get_char(&buffer) != PAM_AUTHTOK)
363                                 goto fail;
364                         reply[i].resp = buffer_get_string(&buffer, NULL);
365                         break;
366                 case PAM_ERROR_MSG:
367                         buffer_put_cstring(&buffer,
368                             PAM_MSG_MEMBER(msg, i, msg));
369                         if (ssh_msg_send(ctxt->pam_csock,
370                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
371                                 goto fail;
372                         break;
373                 case PAM_TEXT_INFO:
374                         buffer_put_cstring(&buffer,
375                             PAM_MSG_MEMBER(msg, i, msg));
376                         if (ssh_msg_send(ctxt->pam_csock,
377                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
378                                 goto fail;
379                         break;
380                 default:
381                         goto fail;
382                 }
383                 buffer_clear(&buffer);
384         }
385         buffer_free(&buffer);
386         *resp = reply;
387         return (PAM_SUCCESS);
388
389  fail:
390         for(i = 0; i < n; i++) {
391                 if (reply[i].resp != NULL)
392                         xfree(reply[i].resp);
393         }
394         xfree(reply);
395         buffer_free(&buffer);
396         return (PAM_CONV_ERR);
397 }
398
399 /*
400  * Authentication thread.
401  */
402 static void *
403 sshpam_thread(void *ctxtp)
404 {
405         struct pam_ctxt *ctxt = ctxtp;
406         Buffer buffer;
407         struct pam_conv sshpam_conv;
408         int flags = (options.permit_empty_passwd == 0 ?
409             PAM_DISALLOW_NULL_AUTHTOK : 0);
410 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
411         extern char **environ;
412         char **env_from_pam;
413         u_int i;
414         const char *pam_user;
415         const char **ptr_pam_user = &pam_user;
416
417         pam_get_item(sshpam_handle, PAM_USER,
418             (sshpam_const void **)ptr_pam_user);
419         environ[0] = NULL;
420
421         if (sshpam_authctxt != NULL) {
422                 setproctitle("%s [pam]",
423                     sshpam_authctxt->valid ? pam_user : "unknown");
424         }
425 #endif
426
427         sshpam_conv.conv = sshpam_thread_conv;
428         sshpam_conv.appdata_ptr = ctxt;
429
430         if (sshpam_authctxt == NULL)
431                 fatal("%s: PAM authctxt not initialized", __func__);
432
433         buffer_init(&buffer);
434         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
435             (const void *)&sshpam_conv);
436         if (sshpam_err != PAM_SUCCESS)
437                 goto auth_fail;
438         sshpam_err = pam_authenticate(sshpam_handle, flags);
439         if (sshpam_err != PAM_SUCCESS)
440                 goto auth_fail;
441
442         if (compat20) {
443                 if (!do_pam_account())
444                         goto auth_fail;
445                 if (sshpam_authctxt->force_pwchange) {
446                         sshpam_err = pam_chauthtok(sshpam_handle,
447                             PAM_CHANGE_EXPIRED_AUTHTOK);
448                         if (sshpam_err != PAM_SUCCESS)
449                                 goto auth_fail;
450                         sshpam_password_change_required(0);
451                 }
452         }
453
454         buffer_put_cstring(&buffer, "OK");
455
456 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
457         /* Export variables set by do_pam_account */
458         buffer_put_int(&buffer, sshpam_account_status);
459         buffer_put_int(&buffer, sshpam_authctxt->force_pwchange);
460
461         /* Export any environment strings set in child */
462         for(i = 0; environ[i] != NULL; i++)
463                 ; /* Count */
464         buffer_put_int(&buffer, i);
465         for(i = 0; environ[i] != NULL; i++)
466                 buffer_put_cstring(&buffer, environ[i]);
467
468         /* Export any environment strings set by PAM in child */
469         env_from_pam = pam_getenvlist(sshpam_handle);
470         for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
471                 ; /* Count */
472         buffer_put_int(&buffer, i);
473         for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
474                 buffer_put_cstring(&buffer, env_from_pam[i]);
475 #endif /* UNSUPPORTED_POSIX_THREADS_HACK */
476
477         /* XXX - can't do much about an error here */
478         ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);
479         buffer_free(&buffer);
480         pthread_exit(NULL);
481
482  auth_fail:
483         buffer_put_cstring(&buffer,
484             pam_strerror(sshpam_handle, sshpam_err));
485         /* XXX - can't do much about an error here */
486         ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
487         buffer_free(&buffer);
488         pthread_exit(NULL);
489
490         return (NULL); /* Avoid warning for non-pthread case */
491 }
492
493 void
494 sshpam_thread_cleanup(void)
495 {
496         struct pam_ctxt *ctxt = cleanup_ctxt;
497
498         debug3("PAM: %s entering", __func__);
499         if (ctxt != NULL && ctxt->pam_thread != 0) {
500                 pthread_cancel(ctxt->pam_thread);
501                 pthread_join(ctxt->pam_thread, NULL);
502                 close(ctxt->pam_psock);
503                 close(ctxt->pam_csock);
504                 memset(ctxt, 0, sizeof(*ctxt));
505                 cleanup_ctxt = NULL;
506         }
507 }
508
509 static int
510 sshpam_null_conv(int n, sshpam_const struct pam_message **msg,
511     struct pam_response **resp, void *data)
512 {
513         debug3("PAM: %s entering, %d messages", __func__, n);
514         return (PAM_CONV_ERR);
515 }
516
517 static struct pam_conv null_conv = { sshpam_null_conv, NULL };
518
519 static int
520 sshpam_store_conv(int n, sshpam_const struct pam_message **msg,
521     struct pam_response **resp, void *data)
522 {
523         struct pam_response *reply;
524         int i;
525         size_t len;
526
527         debug3("PAM: %s called with %d messages", __func__, n);
528         *resp = NULL;
529
530         if (n <= 0 || n > PAM_MAX_NUM_MSG)
531                 return (PAM_CONV_ERR);
532
533         if ((reply = malloc(n * sizeof(*reply))) == NULL)
534                 return (PAM_CONV_ERR);
535         memset(reply, 0, n * sizeof(*reply));
536
537         for (i = 0; i < n; ++i) {
538                 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
539                 case PAM_ERROR_MSG:
540                 case PAM_TEXT_INFO:
541                         len = strlen(PAM_MSG_MEMBER(msg, i, msg));
542                         buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len);
543                         buffer_append(&loginmsg, "\n", 1 );
544                         reply[i].resp_retcode = PAM_SUCCESS;
545                         break;
546                 default:
547                         goto fail;
548                 }
549         }
550         *resp = reply;
551         return (PAM_SUCCESS);
552
553  fail:
554         for(i = 0; i < n; i++) {
555                 if (reply[i].resp != NULL)
556                         xfree(reply[i].resp);
557         }
558         xfree(reply);
559         return (PAM_CONV_ERR);
560 }
561
562 static struct pam_conv store_conv = { sshpam_store_conv, NULL };
563
564 void
565 sshpam_cleanup(void)
566 {
567         debug("PAM: cleanup");
568         if (sshpam_handle == NULL)
569                 return;
570         pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
571         if (sshpam_cred_established) {
572                 pam_setcred(sshpam_handle, PAM_DELETE_CRED);
573                 sshpam_cred_established = 0;
574         }
575         if (sshpam_session_open) {
576                 pam_close_session(sshpam_handle, PAM_SILENT);
577                 sshpam_session_open = 0;
578         }
579         sshpam_authenticated = 0;
580         pam_end(sshpam_handle, sshpam_err);
581         sshpam_handle = NULL;
582 }
583
584 static int
585 sshpam_init(Authctxt *authctxt)
586 {
587         extern char *__progname;
588         const char *pam_rhost, *pam_user, *user = authctxt->user;
589         const char **ptr_pam_user = &pam_user;
590
591         if (sshpam_handle != NULL) {
592                 /* We already have a PAM context; check if the user matches */
593                 sshpam_err = pam_get_item(sshpam_handle,
594                     PAM_USER, (sshpam_const void **)ptr_pam_user);
595                 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
596                         return (0);
597                 pam_end(sshpam_handle, sshpam_err);
598                 sshpam_handle = NULL;
599         }
600         debug("PAM: initializing for \"%s\"", user);
601         sshpam_err =
602             pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle);
603         sshpam_authctxt = authctxt;
604
605         if (sshpam_err != PAM_SUCCESS) {
606                 pam_end(sshpam_handle, sshpam_err);
607                 sshpam_handle = NULL;
608                 return (-1);
609         }
610         pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns);
611         debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost);
612         sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost);
613         if (sshpam_err != PAM_SUCCESS) {
614                 pam_end(sshpam_handle, sshpam_err);
615                 sshpam_handle = NULL;
616                 return (-1);
617         }
618 #ifdef PAM_TTY_KLUDGE
619         /*
620          * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
621          * sshd doesn't set the tty until too late in the auth process and
622          * may not even set one (for tty-less connections)
623          */
624         debug("PAM: setting PAM_TTY to \"ssh\"");
625         sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh");
626         if (sshpam_err != PAM_SUCCESS) {
627                 pam_end(sshpam_handle, sshpam_err);
628                 sshpam_handle = NULL;
629                 return (-1);
630         }
631 #endif
632         return (0);
633 }
634
635 static void *
636 sshpam_init_ctx(Authctxt *authctxt)
637 {
638         struct pam_ctxt *ctxt;
639         int socks[2];
640
641         debug3("PAM: %s entering", __func__);
642         /* Refuse to start if we don't have PAM enabled */
643         if (!options.use_pam)
644                 return NULL;
645
646         /* Initialize PAM */
647         if (sshpam_init(authctxt) == -1) {
648                 error("PAM: initialization failed");
649                 return (NULL);
650         }
651
652         ctxt = xmalloc(sizeof *ctxt);
653         memset(ctxt, 0, sizeof(*ctxt));
654
655         /* Start the authentication thread */
656         if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
657                 error("PAM: failed create sockets: %s", strerror(errno));
658                 xfree(ctxt);
659                 return (NULL);
660         }
661         ctxt->pam_psock = socks[0];
662         ctxt->pam_csock = socks[1];
663         if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) {
664                 error("PAM: failed to start authentication thread: %s",
665                     strerror(errno));
666                 close(socks[0]);
667                 close(socks[1]);
668                 xfree(ctxt);
669                 return (NULL);
670         }
671         cleanup_ctxt = ctxt;
672         return (ctxt);
673 }
674
675 static int
676 sshpam_query(void *ctx, char **name, char **info,
677     u_int *num, char ***prompts, u_int **echo_on)
678 {
679         Buffer buffer;
680         struct pam_ctxt *ctxt = ctx;
681         size_t plen;
682         u_char type;
683         char *msg;
684         size_t len, mlen;
685
686         debug3("PAM: %s entering", __func__);
687         buffer_init(&buffer);
688         *name = xstrdup("");
689         *info = xstrdup("");
690         *prompts = xmalloc(sizeof(char *));
691         **prompts = NULL;
692         plen = 0;
693         *echo_on = xmalloc(sizeof(u_int));
694         while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
695                 type = buffer_get_char(&buffer);
696                 msg = buffer_get_string(&buffer, NULL);
697                 mlen = strlen(msg);
698                 switch (type) {
699                 case PAM_PROMPT_ECHO_ON:
700                 case PAM_PROMPT_ECHO_OFF:
701                         *num = 1;
702                         len = plen + mlen + 1;
703                         **prompts = xrealloc(**prompts, len);
704                         strlcpy(**prompts + plen, msg, len - plen);
705                         plen += mlen;
706                         **echo_on = (type == PAM_PROMPT_ECHO_ON);
707                         xfree(msg);
708                         return (0);
709                 case PAM_ERROR_MSG:
710                 case PAM_TEXT_INFO:
711                         /* accumulate messages */
712                         len = plen + mlen + 2;
713                         **prompts = xrealloc(**prompts, len);
714                         strlcpy(**prompts + plen, msg, len - plen);
715                         plen += mlen;
716                         strlcat(**prompts + plen, "\n", len - plen);
717                         plen++;
718                         xfree(msg);
719                         break;
720                 case PAM_AUTH_ERR:
721                         debug3("PAM: PAM_AUTH_ERR");
722                         if (**prompts != NULL && strlen(**prompts) != 0) {
723                                 *info = **prompts;
724                                 **prompts = NULL;
725                                 *num = 0;
726                                 **echo_on = 0;
727                                 ctxt->pam_done = -1;
728                                 return 0;
729                         }
730                         /* FALLTHROUGH */
731                 case PAM_SUCCESS:
732                         if (**prompts != NULL) {
733                                 /* drain any accumulated messages */
734                                 debug("PAM: %s", **prompts);
735                                 buffer_append(&loginmsg, **prompts,
736                                     strlen(**prompts));
737                                 xfree(**prompts);
738                                 **prompts = NULL;
739                         }
740                         if (type == PAM_SUCCESS) {
741                                 if (!sshpam_authctxt->valid ||
742                                     (sshpam_authctxt->pw->pw_uid == 0 &&
743                                     options.permit_root_login != PERMIT_YES))
744                                         fatal("Internal error: PAM auth "
745                                             "succeeded when it should have "
746                                             "failed");
747                                 import_environments(&buffer);
748                                 *num = 0;
749                                 **echo_on = 0;
750                                 ctxt->pam_done = 1;
751                                 xfree(msg);
752                                 return (0);
753                         }
754                         error("PAM: %s for %s%.100s from %.100s", msg,
755                             sshpam_authctxt->valid ? "" : "illegal user ",
756                             sshpam_authctxt->user,
757                             get_remote_name_or_ip(utmp_len, options.use_dns));
758                         /* FALLTHROUGH */
759                 default:
760                         *num = 0;
761                         **echo_on = 0;
762                         xfree(msg);
763                         ctxt->pam_done = -1;
764                         return (-1);
765                 }
766         }
767         return (-1);
768 }
769
770 /* XXX - see also comment in auth-chall.c:verify_response */
771 static int
772 sshpam_respond(void *ctx, u_int num, char **resp)
773 {
774         Buffer buffer;
775         struct pam_ctxt *ctxt = ctx;
776
777         debug2("PAM: %s entering, %u responses", __func__, num);
778         switch (ctxt->pam_done) {
779         case 1:
780                 sshpam_authenticated = 1;
781                 return (0);
782         case 0:
783                 break;
784         default:
785                 return (-1);
786         }
787         if (num != 1) {
788                 error("PAM: expected one response, got %u", num);
789                 return (-1);
790         }
791         buffer_init(&buffer);
792         if (sshpam_authctxt->valid &&
793             (sshpam_authctxt->pw->pw_uid != 0 ||
794             options.permit_root_login == PERMIT_YES))
795                 buffer_put_cstring(&buffer, *resp);
796         else
797                 buffer_put_cstring(&buffer, badpw);
798         if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) {
799                 buffer_free(&buffer);
800                 return (-1);
801         }
802         buffer_free(&buffer);
803         return (1);
804 }
805
806 static void
807 sshpam_free_ctx(void *ctxtp)
808 {
809         struct pam_ctxt *ctxt = ctxtp;
810
811         debug3("PAM: %s entering", __func__);
812         sshpam_thread_cleanup();
813         xfree(ctxt);
814         /*
815          * We don't call sshpam_cleanup() here because we may need the PAM
816          * handle at a later stage, e.g. when setting up a session.  It's
817          * still on the cleanup list, so pam_end() *will* be called before
818          * the server process terminates.
819          */
820 }
821
822 KbdintDevice sshpam_device = {
823         "pam",
824         sshpam_init_ctx,
825         sshpam_query,
826         sshpam_respond,
827         sshpam_free_ctx
828 };
829
830 KbdintDevice mm_sshpam_device = {
831         "pam",
832         mm_sshpam_init_ctx,
833         mm_sshpam_query,
834         mm_sshpam_respond,
835         mm_sshpam_free_ctx
836 };
837
838 /*
839  * This replaces auth-pam.c
840  */
841 void
842 start_pam(Authctxt *authctxt)
843 {
844         if (!options.use_pam)
845                 fatal("PAM: initialisation requested when UsePAM=no");
846
847         if (sshpam_init(authctxt) == -1)
848                 fatal("PAM: initialisation failed");
849 }
850
851 void
852 finish_pam(void)
853 {
854         sshpam_cleanup();
855 }
856
857 u_int
858 do_pam_account(void)
859 {
860         debug("%s: called", __func__);
861         if (sshpam_account_status != -1)
862                 return (sshpam_account_status);
863
864         sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
865         debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err,
866             pam_strerror(sshpam_handle, sshpam_err));
867
868         if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) {
869                 sshpam_account_status = 0;
870                 return (sshpam_account_status);
871         }
872
873         if (sshpam_err == PAM_NEW_AUTHTOK_REQD)
874                 sshpam_password_change_required(1);
875
876         sshpam_account_status = 1;
877         return (sshpam_account_status);
878 }
879
880 void
881 do_pam_set_tty(const char *tty)
882 {
883         if (tty != NULL) {
884                 debug("PAM: setting PAM_TTY to \"%s\"", tty);
885                 sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty);
886                 if (sshpam_err != PAM_SUCCESS)
887                         fatal("PAM: failed to set PAM_TTY: %s",
888                             pam_strerror(sshpam_handle, sshpam_err));
889         }
890 }
891
892 void
893 do_pam_setcred(int init)
894 {
895         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
896             (const void *)&store_conv);
897         if (sshpam_err != PAM_SUCCESS)
898                 fatal("PAM: failed to set PAM_CONV: %s",
899                     pam_strerror(sshpam_handle, sshpam_err));
900         if (init) {
901                 debug("PAM: establishing credentials");
902                 sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
903         } else {
904                 debug("PAM: reinitializing credentials");
905                 sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
906         }
907         if (sshpam_err == PAM_SUCCESS) {
908                 sshpam_cred_established = 1;
909                 return;
910         }
911         if (sshpam_authenticated)
912                 fatal("PAM: pam_setcred(): %s",
913                     pam_strerror(sshpam_handle, sshpam_err));
914         else
915                 debug("PAM: pam_setcred(): %s",
916                     pam_strerror(sshpam_handle, sshpam_err));
917 }
918
919 static int
920 sshpam_tty_conv(int n, sshpam_const struct pam_message **msg,
921     struct pam_response **resp, void *data)
922 {
923         char input[PAM_MAX_MSG_SIZE];
924         struct pam_response *reply;
925         int i;
926
927         debug3("PAM: %s called with %d messages", __func__, n);
928
929         *resp = NULL;
930
931         if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO))
932                 return (PAM_CONV_ERR);
933
934         if ((reply = malloc(n * sizeof(*reply))) == NULL)
935                 return (PAM_CONV_ERR);
936         memset(reply, 0, n * sizeof(*reply));
937
938         for (i = 0; i < n; ++i) {
939                 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
940                 case PAM_PROMPT_ECHO_OFF:
941                         reply[i].resp =
942                             read_passphrase(PAM_MSG_MEMBER(msg, i, msg),
943                             RP_ALLOW_STDIN);
944                         reply[i].resp_retcode = PAM_SUCCESS;
945                         break;
946                 case PAM_PROMPT_ECHO_ON:
947                         fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
948                         fgets(input, sizeof input, stdin);
949                         if ((reply[i].resp = strdup(input)) == NULL)
950                                 goto fail;
951                         reply[i].resp_retcode = PAM_SUCCESS;
952                         break;
953                 case PAM_ERROR_MSG:
954                 case PAM_TEXT_INFO:
955                         fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
956                         reply[i].resp_retcode = PAM_SUCCESS;
957                         break;
958                 default:
959                         goto fail;
960                 }
961         }
962         *resp = reply;
963         return (PAM_SUCCESS);
964
965  fail:
966         for(i = 0; i < n; i++) {
967                 if (reply[i].resp != NULL)
968                         xfree(reply[i].resp);
969         }
970         xfree(reply);
971         return (PAM_CONV_ERR);
972 }
973
974 static struct pam_conv tty_conv = { sshpam_tty_conv, NULL };
975
976 /*
977  * XXX this should be done in the authentication phase, but ssh1 doesn't
978  * support that
979  */
980 void
981 do_pam_chauthtok(void)
982 {
983         if (use_privsep)
984                 fatal("Password expired (unable to change with privsep)");
985         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
986             (const void *)&tty_conv);
987         if (sshpam_err != PAM_SUCCESS)
988                 fatal("PAM: failed to set PAM_CONV: %s",
989                     pam_strerror(sshpam_handle, sshpam_err));
990         debug("PAM: changing password");
991         sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
992         if (sshpam_err != PAM_SUCCESS)
993                 fatal("PAM: pam_chauthtok(): %s",
994                     pam_strerror(sshpam_handle, sshpam_err));
995 }
996
997 void
998 do_pam_session(void)
999 {
1000         debug3("PAM: opening session");
1001         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
1002             (const void *)&store_conv);
1003         if (sshpam_err != PAM_SUCCESS)
1004                 fatal("PAM: failed to set PAM_CONV: %s",
1005                     pam_strerror(sshpam_handle, sshpam_err));
1006         sshpam_err = pam_open_session(sshpam_handle, 0);
1007         if (sshpam_err == PAM_SUCCESS)
1008                 sshpam_session_open = 1;
1009         else {
1010                 sshpam_session_open = 0;
1011                 disable_forwarding();
1012                 error("PAM: pam_open_session(): %s",
1013                     pam_strerror(sshpam_handle, sshpam_err));
1014         }
1015
1016 }
1017
1018 int
1019 is_pam_session_open(void)
1020 {
1021         return sshpam_session_open;
1022 }
1023
1024 /*
1025  * Set a PAM environment string. We need to do this so that the session
1026  * modules can handle things like Kerberos/GSI credentials that appear
1027  * during the ssh authentication process.
1028  */
1029 int
1030 do_pam_putenv(char *name, char *value)
1031 {
1032         int ret = 1;
1033 #ifdef HAVE_PAM_PUTENV
1034         char *compound;
1035         size_t len;
1036
1037         len = strlen(name) + strlen(value) + 2;
1038         compound = xmalloc(len);
1039
1040         snprintf(compound, len, "%s=%s", name, value);
1041         ret = pam_putenv(sshpam_handle, compound);
1042         xfree(compound);
1043 #endif
1044
1045         return (ret);
1046 }
1047
1048 char **
1049 fetch_pam_child_environment(void)
1050 {
1051         return sshpam_env;
1052 }
1053
1054 char **
1055 fetch_pam_environment(void)
1056 {
1057         return (pam_getenvlist(sshpam_handle));
1058 }
1059
1060 void
1061 free_pam_environment(char **env)
1062 {
1063         char **envp;
1064
1065         if (env == NULL)
1066                 return;
1067
1068         for (envp = env; *envp; envp++)
1069                 xfree(*envp);
1070         xfree(env);
1071 }
1072
1073 /*
1074  * "Blind" conversation function for password authentication.  Assumes that
1075  * echo-off prompts are for the password and stores messages for later
1076  * display.
1077  */
1078 static int
1079 sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg,
1080     struct pam_response **resp, void *data)
1081 {
1082         struct pam_response *reply;
1083         int i;
1084         size_t len;
1085
1086         debug3("PAM: %s called with %d messages", __func__, n);
1087
1088         *resp = NULL;
1089
1090         if (n <= 0 || n > PAM_MAX_NUM_MSG)
1091                 return (PAM_CONV_ERR);
1092
1093         if ((reply = malloc(n * sizeof(*reply))) == NULL)
1094                 return (PAM_CONV_ERR);
1095         memset(reply, 0, n * sizeof(*reply));
1096
1097         for (i = 0; i < n; ++i) {
1098                 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
1099                 case PAM_PROMPT_ECHO_OFF:
1100                         if (sshpam_password == NULL)
1101                                 goto fail;
1102                         if ((reply[i].resp = strdup(sshpam_password)) == NULL)
1103                                 goto fail;
1104                         reply[i].resp_retcode = PAM_SUCCESS;
1105                         break;
1106                 case PAM_ERROR_MSG:
1107                 case PAM_TEXT_INFO:
1108                         len = strlen(PAM_MSG_MEMBER(msg, i, msg));
1109                         if (len > 0) {
1110                                 buffer_append(&loginmsg,
1111                                     PAM_MSG_MEMBER(msg, i, msg), len);
1112                                 buffer_append(&loginmsg, "\n", 1);
1113                         }
1114                         if ((reply[i].resp = strdup("")) == NULL)
1115                                 goto fail;
1116                         reply[i].resp_retcode = PAM_SUCCESS;
1117                         break;
1118                 default:
1119                         goto fail;
1120                 }
1121         }
1122         *resp = reply;
1123         return (PAM_SUCCESS);
1124
1125  fail:
1126         for(i = 0; i < n; i++) {
1127                 if (reply[i].resp != NULL)
1128                         xfree(reply[i].resp);
1129         }
1130         xfree(reply);
1131         return (PAM_CONV_ERR);
1132 }
1133
1134 static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL };
1135
1136 /*
1137  * Attempt password authentication via PAM
1138  */
1139 int
1140 sshpam_auth_passwd(Authctxt *authctxt, const char *password)
1141 {
1142         int flags = (options.permit_empty_passwd == 0 ?
1143             PAM_DISALLOW_NULL_AUTHTOK : 0);
1144
1145         if (!options.use_pam || sshpam_handle == NULL)
1146                 fatal("PAM: %s called when PAM disabled or failed to "
1147                     "initialise.", __func__);
1148
1149         sshpam_password = password;
1150         sshpam_authctxt = authctxt;
1151
1152         /*
1153          * If the user logging in is invalid, or is root but is not permitted
1154          * by PermitRootLogin, use an invalid password to prevent leaking
1155          * information via timing (eg if the PAM config has a delay on fail).
1156          */
1157         if (!authctxt->valid || (authctxt->pw->pw_uid == 0 &&
1158             options.permit_root_login != PERMIT_YES))
1159                 sshpam_password = badpw;
1160
1161         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
1162             (const void *)&passwd_conv);
1163         if (sshpam_err != PAM_SUCCESS)
1164                 fatal("PAM: %s: failed to set PAM_CONV: %s", __func__,
1165                     pam_strerror(sshpam_handle, sshpam_err));
1166
1167         sshpam_err = pam_authenticate(sshpam_handle, flags);
1168         sshpam_password = NULL;
1169         if (sshpam_err == PAM_SUCCESS && authctxt->valid) {
1170                 debug("PAM: password authentication accepted for %.100s",
1171                     authctxt->user);
1172                 return 1;
1173         } else {
1174                 debug("PAM: password authentication failed for %.100s: %s",
1175                     authctxt->valid ? authctxt->user : "an illegal user",
1176                     pam_strerror(sshpam_handle, sshpam_err));
1177                 return 0;
1178         }
1179 }
1180 #endif /* USE_PAM */