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