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