]> andersk Git - openssh.git/blob - auth-pam.c
- (dtucker) [auth-pam.c] Convert chauthtok_conv into a generic tty_conv,
[openssh.git] / 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 /* Based on $FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */
33 #include "includes.h"
34 RCSID("$Id$");
35
36 #ifdef USE_PAM
37 #include <security/pam_appl.h>
38
39 #include "auth.h"
40 #include "auth-pam.h"
41 #include "buffer.h"
42 #include "bufaux.h"
43 #include "canohost.h"
44 #include "log.h"
45 #include "monitor_wrap.h"
46 #include "msg.h"
47 #include "packet.h"
48 #include "readpass.h"
49 #include "servconf.h"
50 #include "ssh2.h"
51 #include "xmalloc.h"
52 #include "auth-options.h"
53
54 extern ServerOptions options;
55 extern Buffer loginmsg;
56
57 #define __unused
58
59 #ifdef USE_POSIX_THREADS
60 #include <pthread.h>
61 /*
62  * Avoid namespace clash when *not* using pthreads for systems *with* 
63  * pthreads, which unconditionally define pthread_t via sys/types.h 
64  * (e.g. Linux)
65  */
66 typedef pthread_t sp_pthread_t; 
67 #else
68 /*
69  * Simulate threads with processes.
70  */
71 typedef pid_t sp_pthread_t;
72
73 static void
74 pthread_exit(void *value __unused)
75 {
76         _exit(0);
77 }
78
79 static int
80 pthread_create(sp_pthread_t *thread, const void *attr __unused,
81     void *(*thread_start)(void *), void *arg)
82 {
83         pid_t pid;
84
85         switch ((pid = fork())) {
86         case -1:
87                 error("fork(): %s", strerror(errno));
88                 return (-1);
89         case 0:
90                 thread_start(arg);
91                 _exit(1);
92         default:
93                 *thread = pid;
94                 return (0);
95         }
96 }
97
98 static int
99 pthread_cancel(sp_pthread_t thread)
100 {
101         return (kill(thread, SIGTERM));
102 }
103
104 static int
105 pthread_join(sp_pthread_t thread, void **value __unused)
106 {
107         int status;
108
109         waitpid(thread, &status, 0);
110         return (status);
111 }
112 #endif
113
114
115 static pam_handle_t *sshpam_handle = NULL;
116 static int sshpam_err = 0;
117 static int sshpam_authenticated = 0;
118 static int sshpam_new_authtok_reqd = 0;
119 static int sshpam_session_open = 0;
120 static int sshpam_cred_established = 0;
121 static char **sshpam_env = NULL;
122
123 struct pam_ctxt {
124         sp_pthread_t     pam_thread;
125         int              pam_psock;
126         int              pam_csock;
127         int              pam_done;
128 };
129
130 static void sshpam_free_ctx(void *);
131 static struct pam_ctxt *cleanup_ctxt;
132
133 /* Some PAM implementations don't implement this */
134 #ifndef HAVE_PAM_GETENVLIST
135 static char **
136 pam_getenvlist(pam_handle_t *pamh)
137 {
138         /*
139          * XXX - If necessary, we can still support envrionment passing 
140          * for platforms without pam_getenvlist by searching for known
141          * env vars (e.g. KRB5CCNAME) from the PAM environment.
142          */
143          return NULL;
144 }
145 #endif
146
147 /* Import regular and PAM environment from subprocess */
148 static void
149 import_environments(Buffer *b)
150 {
151         char *env;
152         u_int i, num_env;
153         int err;
154
155         /* Import environment from subprocess */
156         num_env = buffer_get_int(b);
157         sshpam_env = xmalloc((num_env + 1) * sizeof(*sshpam_env));
158         debug3("PAM: num env strings %d", num_env);
159         for(i = 0; i < num_env; i++)
160                 sshpam_env[i] = buffer_get_string(b, NULL);
161
162         sshpam_env[num_env] = NULL;
163
164         /* Import PAM environment from subprocess */
165         num_env = buffer_get_int(b);
166         debug("PAM: num PAM env strings %d", num_env);
167         for(i = 0; i < num_env; i++) {
168                 env = buffer_get_string(b, NULL);
169
170                 /* Errors are not fatal here */
171                 if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) {
172                         error("PAM: pam_putenv: %s",
173                             pam_strerror(sshpam_handle, sshpam_err));
174                 }
175         }
176 }
177
178 /*
179  * Conversation function for authentication thread.
180  */
181 static int
182 sshpam_thread_conv(int n, const struct pam_message **msg,
183     struct pam_response **resp, void *data)
184 {
185         Buffer buffer;
186         struct pam_ctxt *ctxt;
187         struct pam_response *reply;
188         int i;
189
190         *resp = NULL;
191
192         ctxt = data;
193         if (n <= 0 || n > PAM_MAX_NUM_MSG)
194                 return (PAM_CONV_ERR);
195
196         if ((reply = malloc(n * sizeof(*reply))) == NULL)
197                 return (PAM_CONV_ERR);
198         memset(reply, 0, n * sizeof(*reply));
199
200         buffer_init(&buffer);
201         for (i = 0; i < n; ++i) {
202                 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
203                 case PAM_PROMPT_ECHO_OFF:
204                         buffer_put_cstring(&buffer, 
205                             PAM_MSG_MEMBER(msg, i, msg));
206                         if (ssh_msg_send(ctxt->pam_csock, 
207                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
208                                 goto fail;
209                         if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1) 
210                                 goto fail;
211                         if (buffer_get_char(&buffer) != PAM_AUTHTOK)
212                                 goto fail;
213                         reply[i].resp = buffer_get_string(&buffer, NULL);
214                         break;
215                 case PAM_PROMPT_ECHO_ON:
216                         buffer_put_cstring(&buffer, 
217                             PAM_MSG_MEMBER(msg, i, msg));
218                         if (ssh_msg_send(ctxt->pam_csock, 
219                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
220                                 goto fail;
221                         if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
222                                 goto fail;
223                         if (buffer_get_char(&buffer) != PAM_AUTHTOK)
224                                 goto fail;
225                         reply[i].resp = buffer_get_string(&buffer, NULL);
226                         break;
227                 case PAM_ERROR_MSG:
228                         buffer_put_cstring(&buffer, 
229                             PAM_MSG_MEMBER(msg, i, msg));
230                         if (ssh_msg_send(ctxt->pam_csock, 
231                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
232                                 goto fail;
233                         break;
234                 case PAM_TEXT_INFO:
235                         buffer_put_cstring(&buffer, 
236                             PAM_MSG_MEMBER(msg, i, msg));
237                         if (ssh_msg_send(ctxt->pam_csock, 
238                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
239                                 goto fail;
240                         break;
241                 default:
242                         goto fail;
243                 }
244                 buffer_clear(&buffer);
245         }
246         buffer_free(&buffer);
247         *resp = reply;
248         return (PAM_SUCCESS);
249
250  fail:
251         for(i = 0; i < n; i++) {
252                 if (reply[i].resp != NULL)
253                         xfree(reply[i].resp);
254         }
255         xfree(reply);
256         buffer_free(&buffer);
257         return (PAM_CONV_ERR);
258 }
259
260 /*
261  * Authentication thread.
262  */
263 static void *
264 sshpam_thread(void *ctxtp)
265 {
266         struct pam_ctxt *ctxt = ctxtp;
267         Buffer buffer;
268         struct pam_conv sshpam_conv;
269 #ifndef USE_POSIX_THREADS
270         extern char **environ;
271         char **env_from_pam;
272         u_int i;
273         const char *pam_user;
274
275         pam_get_item(sshpam_handle, PAM_USER, (const void **)&pam_user);
276         setproctitle("%s [pam]", pam_user);
277         environ[0] = NULL;
278 #endif
279
280         sshpam_conv.conv = sshpam_thread_conv;
281         sshpam_conv.appdata_ptr = ctxt;
282
283         buffer_init(&buffer);
284         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
285             (const void *)&sshpam_conv);
286         if (sshpam_err != PAM_SUCCESS)
287                 goto auth_fail;
288         sshpam_err = pam_authenticate(sshpam_handle, 0);
289         if (sshpam_err != PAM_SUCCESS)
290                 goto auth_fail;
291         buffer_put_cstring(&buffer, "OK");
292
293 #ifndef USE_POSIX_THREADS
294         /* Export any environment strings set in child */
295         for(i = 0; environ[i] != NULL; i++)
296                 ; /* Count */
297         buffer_put_int(&buffer, i);
298         for(i = 0; environ[i] != NULL; i++)
299                 buffer_put_cstring(&buffer, environ[i]);
300
301         /* Export any environment strings set by PAM in child */
302         env_from_pam = pam_getenvlist(sshpam_handle);
303         for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
304                 ; /* Count */
305         buffer_put_int(&buffer, i);
306         for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
307                 buffer_put_cstring(&buffer, env_from_pam[i]);
308 #endif /* USE_POSIX_THREADS */
309
310         /* XXX - can't do much about an error here */
311         ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);
312         buffer_free(&buffer);
313         pthread_exit(NULL);
314
315  auth_fail:
316         buffer_put_cstring(&buffer,
317             pam_strerror(sshpam_handle, sshpam_err));
318         /* XXX - can't do much about an error here */
319         ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
320         buffer_free(&buffer);
321         pthread_exit(NULL);
322         
323         return (NULL); /* Avoid warning for non-pthread case */
324 }
325
326 void
327 sshpam_thread_cleanup(void)
328 {
329         struct pam_ctxt *ctxt = cleanup_ctxt;
330
331         if (ctxt != NULL && ctxt->pam_thread != 0) {
332                 pthread_cancel(ctxt->pam_thread);
333                 pthread_join(ctxt->pam_thread, NULL);
334                 close(ctxt->pam_psock);
335                 close(ctxt->pam_csock);
336                 memset(ctxt, 0, sizeof(*ctxt));
337                 cleanup_ctxt = NULL;
338         }
339 }
340
341 static int
342 sshpam_null_conv(int n, const struct pam_message **msg,
343     struct pam_response **resp, void *data)
344 {
345         return (PAM_CONV_ERR);
346 }
347
348 static struct pam_conv null_conv = { sshpam_null_conv, NULL };
349
350 void
351 sshpam_cleanup(void)
352 {
353         debug("PAM: cleanup");
354         if (sshpam_handle == NULL)
355                 return;
356         pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
357         if (sshpam_cred_established) {
358                 pam_setcred(sshpam_handle, PAM_DELETE_CRED);
359                 sshpam_cred_established = 0;
360         }
361         if (sshpam_session_open) {
362                 pam_close_session(sshpam_handle, PAM_SILENT);
363                 sshpam_session_open = 0;
364         }
365         sshpam_authenticated = sshpam_new_authtok_reqd = 0;
366         pam_end(sshpam_handle, sshpam_err);
367         sshpam_handle = NULL;
368 }
369
370 static int
371 sshpam_init(const char *user)
372 {
373         extern u_int utmp_len;
374         extern char *__progname;
375         const char *pam_rhost, *pam_user;
376
377         if (sshpam_handle != NULL) {
378                 /* We already have a PAM context; check if the user matches */
379                 sshpam_err = pam_get_item(sshpam_handle,
380                     PAM_USER, (const void **)&pam_user);
381                 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
382                         return (0);
383                 pam_end(sshpam_handle, sshpam_err);
384                 sshpam_handle = NULL;
385         }
386         debug("PAM: initializing for \"%s\"", user);
387         sshpam_err =
388             pam_start(SSHD_PAM_SERVICE, user, &null_conv, &sshpam_handle);
389         if (sshpam_err != PAM_SUCCESS) {
390                 pam_end(sshpam_handle, sshpam_err);
391                 sshpam_handle = NULL;
392                 return (-1);
393         }
394         pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns);
395         debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost);
396         sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost);
397         if (sshpam_err != PAM_SUCCESS) {
398                 pam_end(sshpam_handle, sshpam_err);
399                 sshpam_handle = NULL;
400                 return (-1);
401         }
402 #ifdef PAM_TTY_KLUDGE
403         /*
404          * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
405          * sshd doesn't set the tty until too late in the auth process and 
406          * may not even set one (for tty-less connections)
407          */
408         debug("PAM: setting PAM_TTY to \"ssh\"");
409         sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh");
410         if (sshpam_err != PAM_SUCCESS) {
411                 pam_end(sshpam_handle, sshpam_err);
412                 sshpam_handle = NULL;
413                 return (-1);
414         }
415 #endif
416         return (0);
417 }
418
419 static void *
420 sshpam_init_ctx(Authctxt *authctxt)
421 {
422         struct pam_ctxt *ctxt;
423         int socks[2];
424
425         /* Refuse to start if we don't have PAM enabled */
426         if (!options.use_pam)
427                 return NULL;
428
429         /* Initialize PAM */
430         if (sshpam_init(authctxt->user) == -1) {
431                 error("PAM: initialization failed");
432                 return (NULL);
433         }
434
435         ctxt = xmalloc(sizeof *ctxt);
436         memset(ctxt, 0, sizeof(*ctxt));
437
438         /* Start the authentication thread */
439         if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
440                 error("PAM: failed create sockets: %s", strerror(errno));
441                 xfree(ctxt);
442                 return (NULL);
443         }
444         ctxt->pam_psock = socks[0];
445         ctxt->pam_csock = socks[1];
446         if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) {
447                 error("PAM: failed to start authentication thread: %s",
448                     strerror(errno));
449                 close(socks[0]);
450                 close(socks[1]);
451                 xfree(ctxt);
452                 return (NULL);
453         }
454         cleanup_ctxt = ctxt;
455         return (ctxt);
456 }
457
458 static int
459 sshpam_query(void *ctx, char **name, char **info,
460     u_int *num, char ***prompts, u_int **echo_on)
461 {
462         Buffer buffer;
463         struct pam_ctxt *ctxt = ctx;
464         size_t plen;
465         u_char type;
466         char *msg;
467         size_t len;
468
469         buffer_init(&buffer);
470         *name = xstrdup("");
471         *info = xstrdup("");
472         *prompts = xmalloc(sizeof(char *));
473         **prompts = NULL;
474         plen = 0;
475         *echo_on = xmalloc(sizeof(u_int));
476         while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
477                 type = buffer_get_char(&buffer);
478                 msg = buffer_get_string(&buffer, NULL);
479                 switch (type) {
480                 case PAM_PROMPT_ECHO_ON:
481                 case PAM_PROMPT_ECHO_OFF:
482                         *num = 1;
483                         len = plen + strlen(msg) + 1;
484                         **prompts = xrealloc(**prompts, len);
485                         plen += snprintf(**prompts + plen, len, "%s", msg);
486                         **echo_on = (type == PAM_PROMPT_ECHO_ON);
487                         xfree(msg);
488                         return (0);
489                 case PAM_ERROR_MSG:
490                 case PAM_TEXT_INFO:
491                         /* accumulate messages */
492                         len = plen + strlen(msg) + 2;
493                         **prompts = xrealloc(**prompts, len);
494                         plen += snprintf(**prompts + plen, len, "%s\n", msg);
495                         xfree(msg);
496                         break;
497                 case PAM_SUCCESS:
498                 case PAM_AUTH_ERR:
499                         if (**prompts != NULL) {
500                                 /* drain any accumulated messages */
501                                 debug("PAM: %s", **prompts);
502                                 buffer_append(&loginmsg, **prompts,
503                                     strlen(**prompts));
504                                 xfree(**prompts);
505                                 **prompts = NULL;
506                         }
507                         if (type == PAM_SUCCESS) {
508                                 import_environments(&buffer);
509                                 *num = 0;
510                                 **echo_on = 0;
511                                 ctxt->pam_done = 1;
512                                 xfree(msg);
513                                 return (0);
514                         }
515                         error("PAM: %s", msg);
516                         /* FALLTHROUGH */
517                 default:
518                         *num = 0;
519                         **echo_on = 0;
520                         xfree(msg);
521                         ctxt->pam_done = -1;
522                         return (-1);
523                 }
524         }
525         return (-1);
526 }
527
528 /* XXX - see also comment in auth-chall.c:verify_response */
529 static int
530 sshpam_respond(void *ctx, u_int num, char **resp)
531 {
532         Buffer buffer;
533         struct pam_ctxt *ctxt = ctx;
534
535         debug2("PAM: %s", __func__);
536         switch (ctxt->pam_done) {
537         case 1:
538                 sshpam_authenticated = 1;
539                 return (0);
540         case 0:
541                 break;
542         default:
543                 return (-1);
544         }
545         if (num != 1) {
546                 error("PAM: expected one response, got %u", num);
547                 return (-1);
548         }
549         buffer_init(&buffer);
550         buffer_put_cstring(&buffer, *resp);
551         if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) {
552                 buffer_free(&buffer);
553                 return (-1);
554         }
555         buffer_free(&buffer);
556         return (1);
557 }
558
559 static void
560 sshpam_free_ctx(void *ctxtp)
561 {
562         struct pam_ctxt *ctxt = ctxtp;
563
564         sshpam_thread_cleanup();
565         xfree(ctxt);
566         /*
567          * We don't call sshpam_cleanup() here because we may need the PAM
568          * handle at a later stage, e.g. when setting up a session.  It's
569          * still on the cleanup list, so pam_end() *will* be called before
570          * the server process terminates.
571          */
572 }
573
574 KbdintDevice sshpam_device = {
575         "pam",
576         sshpam_init_ctx,
577         sshpam_query,
578         sshpam_respond,
579         sshpam_free_ctx
580 };
581
582 KbdintDevice mm_sshpam_device = {
583         "pam",
584         mm_sshpam_init_ctx,
585         mm_sshpam_query,
586         mm_sshpam_respond,
587         mm_sshpam_free_ctx
588 };
589
590 /*
591  * This replaces auth-pam.c
592  */
593 void
594 start_pam(const char *user)
595 {
596         if (!options.use_pam)
597                 fatal("PAM: initialisation requested when UsePAM=no");
598
599         if (sshpam_init(user) == -1)
600                 fatal("PAM: initialisation failed");
601 }
602
603 void
604 finish_pam(void)
605 {
606         sshpam_cleanup();
607 }
608
609 u_int
610 do_pam_account(void)
611 {
612         sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
613         debug3("%s: pam_acct_mgmt = %d", __func__, sshpam_err);
614         
615         if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD)
616                 return (0);
617
618         if (sshpam_err == PAM_NEW_AUTHTOK_REQD) {
619                 sshpam_new_authtok_reqd = 1;
620
621                 /* Prevent forwardings until password changed */
622                 no_port_forwarding_flag |= 2;
623                 no_agent_forwarding_flag |= 2;
624                 no_x11_forwarding_flag |= 2;
625         }
626
627         return (1);
628 }
629
630 void
631 do_pam_set_tty(const char *tty)
632 {
633         if (tty != NULL) {
634                 debug("PAM: setting PAM_TTY to \"%s\"", tty);
635                 sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty);
636                 if (sshpam_err != PAM_SUCCESS)
637                         fatal("PAM: failed to set PAM_TTY: %s",
638                             pam_strerror(sshpam_handle, sshpam_err));
639         }
640 }
641
642 void
643 do_pam_setcred(int init)
644 {
645         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
646             (const void *)&null_conv);
647         if (sshpam_err != PAM_SUCCESS)
648                 fatal("PAM: failed to set PAM_CONV: %s",
649                     pam_strerror(sshpam_handle, sshpam_err));
650         if (init) {
651                 debug("PAM: establishing credentials");
652                 sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
653         } else {
654                 debug("PAM: reinitializing credentials");
655                 sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
656         }
657         if (sshpam_err == PAM_SUCCESS) {
658                 sshpam_cred_established = 1;
659                 return;
660         }
661         if (sshpam_authenticated)
662                 fatal("PAM: pam_setcred(): %s",
663                     pam_strerror(sshpam_handle, sshpam_err));
664         else
665                 debug("PAM: pam_setcred(): %s",
666                     pam_strerror(sshpam_handle, sshpam_err));
667 }
668
669 int
670 is_pam_password_change_required(void)
671 {
672         return (sshpam_new_authtok_reqd);
673 }
674
675 static int
676 pam_tty_conv(int n, const struct pam_message **msg,
677     struct pam_response **resp, void *data)
678 {
679         char input[PAM_MAX_MSG_SIZE];
680         struct pam_response *reply;
681         int i;
682
683         *resp = NULL;
684
685         if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO))
686                 return (PAM_CONV_ERR);
687
688         if ((reply = malloc(n * sizeof(*reply))) == NULL)
689                 return (PAM_CONV_ERR);
690         memset(reply, 0, n * sizeof(*reply));
691
692         for (i = 0; i < n; ++i) {
693                 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
694                 case PAM_PROMPT_ECHO_OFF:
695                         reply[i].resp =
696                             read_passphrase(PAM_MSG_MEMBER(msg, i, msg), 
697                             RP_ALLOW_STDIN);
698                         reply[i].resp_retcode = PAM_SUCCESS;
699                         break;
700                 case PAM_PROMPT_ECHO_ON:
701                         fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
702                         fgets(input, sizeof input, stdin);
703                         reply[i].resp = xstrdup(input);
704                         reply[i].resp_retcode = PAM_SUCCESS;
705                         break;
706                 case PAM_ERROR_MSG:
707                 case PAM_TEXT_INFO:
708                         fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
709                         reply[i].resp_retcode = PAM_SUCCESS;
710                         break;
711                 default:
712                         goto fail;
713                 }
714         }
715         *resp = reply;
716         return (PAM_SUCCESS);
717
718  fail:
719         for(i = 0; i < n; i++) {
720                 if (reply[i].resp != NULL)
721                         xfree(reply[i].resp);
722         }
723         xfree(reply);
724         return (PAM_CONV_ERR);
725 }
726
727 static struct pam_conv tty_conv = { pam_tty_conv, NULL };
728
729 /*
730  * XXX this should be done in the authentication phase, but ssh1 doesn't
731  * support that
732  */
733 void
734 do_pam_chauthtok(void)
735 {
736         if (use_privsep)
737                 fatal("Password expired (unable to change with privsep)");
738         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
739             (const void *)&tty_conv);
740         if (sshpam_err != PAM_SUCCESS)
741                 fatal("PAM: failed to set PAM_CONV: %s",
742                     pam_strerror(sshpam_handle, sshpam_err));
743         debug("PAM: changing password");
744         sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
745         if (sshpam_err != PAM_SUCCESS)
746                 fatal("PAM: pam_chauthtok(): %s",
747                     pam_strerror(sshpam_handle, sshpam_err));
748 }
749
750 void
751 do_pam_session(void)
752 {
753         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 
754             (const void *)&tty_conv);
755         if (sshpam_err != PAM_SUCCESS)
756                 fatal("PAM: failed to set PAM_CONV: %s",
757                     pam_strerror(sshpam_handle, sshpam_err));
758         sshpam_err = pam_open_session(sshpam_handle, 0);
759         if (sshpam_err != PAM_SUCCESS)
760                 fatal("PAM: pam_open_session(): %s",
761                     pam_strerror(sshpam_handle, sshpam_err));
762         sshpam_session_open = 1;
763 }
764
765 /* 
766  * Set a PAM environment string. We need to do this so that the session
767  * modules can handle things like Kerberos/GSI credentials that appear
768  * during the ssh authentication process.
769  */
770 int
771 do_pam_putenv(char *name, char *value) 
772 {
773         int ret = 1;
774 #ifdef HAVE_PAM_PUTENV  
775         char *compound;
776         size_t len;
777
778         len = strlen(name) + strlen(value) + 2;
779         compound = xmalloc(len);
780
781         snprintf(compound, len, "%s=%s", name, value);
782         ret = pam_putenv(sshpam_handle, compound);
783         xfree(compound);
784 #endif
785
786         return (ret);
787 }
788
789 void
790 print_pam_messages(void)
791 {
792         /* XXX */
793 }
794
795 char **
796 fetch_pam_child_environment(void)
797 {
798         return sshpam_env;
799 }
800
801 char **
802 fetch_pam_environment(void)
803 {
804         return (pam_getenvlist(sshpam_handle));
805 }
806
807 void
808 free_pam_environment(char **env)
809 {
810         char **envp;
811
812         if (env == NULL)
813                 return;
814
815         for (envp = env; *envp; envp++)
816                 xfree(*envp);
817         xfree(env);
818 }
819
820 #endif /* USE_PAM */
This page took 0.170872 seconds and 5 git commands to generate.