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