Main Page | Namespace List | Class List | Directories | File List | Class Members | File Members

auth-pam.c

Go to the documentation of this file.
00001 /*-
00002  * Copyright (c) 2002 Networks Associates Technology, Inc.
00003  * All rights reserved.
00004  *
00005  * This software was developed for the FreeBSD Project by ThinkSec AS and
00006  * NAI Labs, the Security Research Division of Network Associates, Inc.
00007  * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
00008  * DARPA CHATS research program.
00009  *
00010  * Redistribution and use in source and binary forms, with or without
00011  * modification, are permitted provided that the following conditions
00012  * are met:
00013  * 1. Redistributions of source code must retain the above copyright
00014  *    notice, this list of conditions and the following disclaimer.
00015  * 2. Redistributions in binary form must reproduce the above copyright
00016  *    notice, this list of conditions and the following disclaimer in the
00017  *    documentation and/or other materials provided with the distribution.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
00020  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00021  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00022  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
00023  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00024  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00025  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00026  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00027  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00028  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00029  * SUCH DAMAGE.
00030  */
00031 /*
00032  * Copyright (c) 2003,2004 Damien Miller <djm@mindrot.org>
00033  * Copyright (c) 2003,2004 Darren Tucker <dtucker@zip.com.au>
00034  *
00035  * Permission to use, copy, modify, and distribute this software for any
00036  * purpose with or without fee is hereby granted, provided that the above
00037  * copyright notice and this permission notice appear in all copies.
00038  *
00039  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
00040  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00041  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
00042  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00043  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00044  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00045  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00046  */
00047 
00048 /* Based on $FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */
00049 #include "includes.h"
00050 RCSID("$Id: auth-pam.c,v 1.128 2006/01/29 05:46:13 dtucker Exp $");
00051 
00052 #ifdef USE_PAM
00053 #if defined(HAVE_SECURITY_PAM_APPL_H)
00054 #include <security/pam_appl.h>
00055 #elif defined (HAVE_PAM_PAM_APPL_H)
00056 #include <pam/pam_appl.h>
00057 #endif
00058 
00059 /* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */
00060 #ifdef PAM_SUN_CODEBASE
00061 # define sshpam_const           /* Solaris, HP-UX, AIX */
00062 #else
00063 # define sshpam_const   const   /* LinuxPAM, OpenPAM */
00064 #endif
00065 
00066 #include "auth.h"
00067 #include "auth-pam.h"
00068 #include "buffer.h"
00069 #include "bufaux.h"
00070 #include "canohost.h"
00071 #include "log.h"
00072 #include "monitor_wrap.h"
00073 #include "msg.h"
00074 #include "packet.h"
00075 #include "misc.h"
00076 #include "servconf.h"
00077 #include "ssh2.h"
00078 #include "xmalloc.h"
00079 #include "auth-options.h"
00080 
00081 extern ServerOptions options;
00082 extern Buffer loginmsg;
00083 extern int compat20;
00084 extern u_int utmp_len;
00085 
00086 /* so we don't silently change behaviour */
00087 #ifdef USE_POSIX_THREADS
00088 # error "USE_POSIX_THREADS replaced by UNSUPPORTED_POSIX_THREADS_HACK"
00089 #endif
00090 
00091 /*
00092  * Formerly known as USE_POSIX_THREADS, using this is completely unsupported
00093  * and generally a bad idea.  Use at own risk and do not expect support if
00094  * this breaks.
00095  */
00096 #ifdef UNSUPPORTED_POSIX_THREADS_HACK
00097 #include <pthread.h>
00098 /*
00099  * Avoid namespace clash when *not* using pthreads for systems *with*
00100  * pthreads, which unconditionally define pthread_t via sys/types.h
00101  * (e.g. Linux)
00102  */
00103 typedef pthread_t sp_pthread_t;
00104 #else
00105 typedef pid_t sp_pthread_t;
00106 #endif
00107 
00108 struct pam_ctxt {
00109         sp_pthread_t     pam_thread;
00110         int              pam_psock;
00111         int              pam_csock;
00112         int              pam_done;
00113 };
00114 
00115 static void sshpam_free_ctx(void *);
00116 static struct pam_ctxt *cleanup_ctxt;
00117 
00118 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
00119 /*
00120  * Simulate threads with processes.
00121  */
00122 
00123 static int sshpam_thread_status = -1;
00124 static mysig_t sshpam_oldsig;
00125 
00126 static void
00127 sshpam_sigchld_handler(int sig)
00128 {
00129         signal(SIGCHLD, SIG_DFL);
00130         if (cleanup_ctxt == NULL)
00131                 return; /* handler called after PAM cleanup, shouldn't happen */
00132         if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG)
00133             <= 0) {
00134                 /* PAM thread has not exitted, privsep slave must have */
00135                 kill(cleanup_ctxt->pam_thread, SIGTERM);
00136                 if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0)
00137                     <= 0)
00138                         return; /* could not wait */
00139         }
00140         if (WIFSIGNALED(sshpam_thread_status) &&
00141             WTERMSIG(sshpam_thread_status) == SIGTERM)
00142                 return; /* terminated by pthread_cancel */
00143         if (!WIFEXITED(sshpam_thread_status))
00144                 fatal("PAM: authentication thread exited unexpectedly");
00145         if (WEXITSTATUS(sshpam_thread_status) != 0)
00146                 fatal("PAM: authentication thread exited uncleanly");
00147 }
00148 
00149 static void
00150 pthread_exit(void *value __unused)
00151 {
00152         _exit(0);
00153 }
00154 
00155 static int
00156 pthread_create(sp_pthread_t *thread, const void *attr __unused,
00157     void *(*thread_start)(void *), void *arg)
00158 {
00159         pid_t pid;
00160         struct pam_ctxt *ctx = arg;
00161 
00162         sshpam_thread_status = -1;
00163         switch ((pid = fork())) {
00164         case -1:
00165                 error("fork(): %s", strerror(errno));
00166                 return (-1);
00167         case 0:
00168                 close(ctx->pam_psock);
00169                 ctx->pam_psock = -1;
00170                 thread_start(arg);
00171                 _exit(1);
00172         default:
00173                 *thread = pid;
00174                 close(ctx->pam_csock);
00175                 ctx->pam_csock = -1;
00176                 sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler);
00177                 return (0);
00178         }
00179 }
00180 
00181 static int
00182 pthread_cancel(sp_pthread_t thread)
00183 {
00184         signal(SIGCHLD, sshpam_oldsig);
00185         return (kill(thread, SIGTERM));
00186 }
00187 
00188 static int
00189 pthread_join(sp_pthread_t thread, void **value __unused)
00190 {
00191         int status;
00192 
00193         if (sshpam_thread_status != -1)
00194                 return (sshpam_thread_status);
00195         signal(SIGCHLD, sshpam_oldsig);
00196         waitpid(thread, &status, 0);
00197         return (status);
00198 }
00199 #endif
00200 
00201 
00202 static pam_handle_t *sshpam_handle = NULL;
00203 static int sshpam_err = 0;
00204 static int sshpam_authenticated = 0;
00205 static int sshpam_session_open = 0;
00206 static int sshpam_cred_established = 0;
00207 static int sshpam_account_status = -1;
00208 static char **sshpam_env = NULL;
00209 static Authctxt *sshpam_authctxt = NULL;
00210 static const char *sshpam_password = NULL;
00211 static char badpw[] = "\b\n\r\177INCORRECT";
00212 
00213 /* Some PAM implementations don't implement this */
00214 #ifndef HAVE_PAM_GETENVLIST
00215 static char **
00216 pam_getenvlist(pam_handle_t *pamh)
00217 {
00218         /*
00219          * XXX - If necessary, we can still support envrionment passing
00220          * for platforms without pam_getenvlist by searching for known
00221          * env vars (e.g. KRB5CCNAME) from the PAM environment.
00222          */
00223          return NULL;
00224 }
00225 #endif
00226 
00227 /*
00228  * Some platforms, notably Solaris, do not enforce password complexity
00229  * rules during pam_chauthtok() if the real uid of the calling process
00230  * is 0, on the assumption that it's being called by "passwd" run by root.
00231  * This wraps pam_chauthtok and sets/restore the real uid so PAM will do
00232  * the right thing.
00233  */
00234 #ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID
00235 static int
00236 sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags)
00237 {
00238         int result;
00239 
00240         if (sshpam_authctxt == NULL)
00241                 fatal("PAM: sshpam_authctxt not initialized");
00242         if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1)
00243                 fatal("%s: setreuid failed: %s", __func__, strerror(errno));
00244         result = pam_chauthtok(pamh, flags);
00245         if (setreuid(0, -1) == -1)
00246                 fatal("%s: setreuid failed: %s", __func__, strerror(errno));
00247         return result;
00248 }
00249 # define pam_chauthtok(a,b)     (sshpam_chauthtok_ruid((a), (b)))
00250 #endif
00251 
00252 void
00253 sshpam_password_change_required(int reqd)
00254 {
00255         debug3("%s %d", __func__, reqd);
00256         if (sshpam_authctxt == NULL)
00257                 fatal("%s: PAM authctxt not initialized", __func__);
00258         sshpam_authctxt->force_pwchange = reqd;
00259         if (reqd) {
00260                 no_port_forwarding_flag |= 2;
00261                 no_agent_forwarding_flag |= 2;
00262                 no_x11_forwarding_flag |= 2;
00263         } else {
00264                 no_port_forwarding_flag &= ~2;
00265                 no_agent_forwarding_flag &= ~2;
00266                 no_x11_forwarding_flag &= ~2;
00267         }
00268 }
00269 
00270 /* Import regular and PAM environment from subprocess */
00271 static void
00272 import_environments(Buffer *b)
00273 {
00274         char *env;
00275         u_int i, num_env;
00276         int err;
00277 
00278         debug3("PAM: %s entering", __func__);
00279 
00280 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
00281         /* Import variables set by do_pam_account */
00282         sshpam_account_status = buffer_get_int(b);
00283         sshpam_password_change_required(buffer_get_int(b));
00284 
00285         /* Import environment from subprocess */
00286         num_env = buffer_get_int(b);
00287         sshpam_env = xmalloc((num_env + 1) * sizeof(*sshpam_env));
00288         debug3("PAM: num env strings %d", num_env);
00289         for(i = 0; i < num_env; i++)
00290                 sshpam_env[i] = buffer_get_string(b, NULL);
00291 
00292         sshpam_env[num_env] = NULL;
00293 
00294         /* Import PAM environment from subprocess */
00295         num_env = buffer_get_int(b);
00296         debug("PAM: num PAM env strings %d", num_env);
00297         for(i = 0; i < num_env; i++) {
00298                 env = buffer_get_string(b, NULL);
00299 
00300 #ifdef HAVE_PAM_PUTENV
00301                 /* Errors are not fatal here */
00302                 if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) {
00303                         error("PAM: pam_putenv: %s",
00304                             pam_strerror(sshpam_handle, sshpam_err));
00305                 }
00306 #endif
00307         }
00308 #endif
00309 }
00310 
00311 /*
00312  * Conversation function for authentication thread.
00313  */
00314 static int
00315 sshpam_thread_conv(int n, sshpam_const struct pam_message **msg,
00316     struct pam_response **resp, void *data)
00317 {
00318         Buffer buffer;
00319         struct pam_ctxt *ctxt;
00320         struct pam_response *reply;
00321         int i;
00322 
00323         debug3("PAM: %s entering, %d messages", __func__, n);
00324         *resp = NULL;
00325 
00326         if (data == NULL) {
00327                 error("PAM: conversation function passed a null context");
00328                 return (PAM_CONV_ERR);
00329         }
00330         ctxt = data;
00331         if (n <= 0 || n > PAM_MAX_NUM_MSG)
00332                 return (PAM_CONV_ERR);
00333 
00334         if ((reply = malloc(n * sizeof(*reply))) == NULL)
00335                 return (PAM_CONV_ERR);
00336         memset(reply, 0, n * sizeof(*reply));
00337 
00338         buffer_init(&buffer);
00339         for (i = 0; i < n; ++i) {
00340                 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
00341                 case PAM_PROMPT_ECHO_OFF:
00342                         buffer_put_cstring(&buffer,
00343                             PAM_MSG_MEMBER(msg, i, msg));
00344                         if (ssh_msg_send(ctxt->pam_csock,
00345                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
00346                                 goto fail;
00347                         if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
00348                                 goto fail;
00349                         if (buffer_get_char(&buffer) != PAM_AUTHTOK)
00350                                 goto fail;
00351                         reply[i].resp = buffer_get_string(&buffer, NULL);
00352                         break;
00353                 case PAM_PROMPT_ECHO_ON:
00354                         buffer_put_cstring(&buffer,
00355                             PAM_MSG_MEMBER(msg, i, msg));
00356                         if (ssh_msg_send(ctxt->pam_csock,
00357                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
00358                                 goto fail;
00359                         if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
00360                                 goto fail;
00361                         if (buffer_get_char(&buffer) != PAM_AUTHTOK)
00362                                 goto fail;
00363                         reply[i].resp = buffer_get_string(&buffer, NULL);
00364                         break;
00365                 case PAM_ERROR_MSG:
00366                         buffer_put_cstring(&buffer,
00367                             PAM_MSG_MEMBER(msg, i, msg));
00368                         if (ssh_msg_send(ctxt->pam_csock,
00369                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
00370                                 goto fail;
00371                         break;
00372                 case PAM_TEXT_INFO:
00373                         buffer_put_cstring(&buffer,
00374                             PAM_MSG_MEMBER(msg, i, msg));
00375                         if (ssh_msg_send(ctxt->pam_csock,
00376                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
00377                                 goto fail;
00378                         break;
00379                 default:
00380                         goto fail;
00381                 }
00382                 buffer_clear(&buffer);
00383         }
00384         buffer_free(&buffer);
00385         *resp = reply;
00386         return (PAM_SUCCESS);
00387 
00388  fail:
00389         for(i = 0; i < n; i++) {
00390                 if (reply[i].resp != NULL)
00391                         xfree(reply[i].resp);
00392         }
00393         xfree(reply);
00394         buffer_free(&buffer);
00395         return (PAM_CONV_ERR);
00396 }
00397 
00398 /*
00399  * Authentication thread.
00400  */
00401 static void *
00402 sshpam_thread(void *ctxtp)
00403 {
00404         struct pam_ctxt *ctxt = ctxtp;
00405         Buffer buffer;
00406         struct pam_conv sshpam_conv;
00407         int flags = (options.permit_empty_passwd == 0 ?
00408             PAM_DISALLOW_NULL_AUTHTOK : 0);
00409 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
00410         extern char **environ;
00411         char **env_from_pam;
00412         u_int i;
00413         const char *pam_user;
00414         const char **ptr_pam_user = &pam_user;
00415 
00416         pam_get_item(sshpam_handle, PAM_USER,
00417             (sshpam_const void **)ptr_pam_user);
00418         environ[0] = NULL;
00419 
00420         if (sshpam_authctxt != NULL) {
00421                 setproctitle("%s [pam]",
00422                     sshpam_authctxt->valid ? pam_user : "unknown");
00423         }
00424 #endif
00425 
00426         sshpam_conv.conv = sshpam_thread_conv;
00427         sshpam_conv.appdata_ptr = ctxt;
00428 
00429         if (sshpam_authctxt == NULL)
00430                 fatal("%s: PAM authctxt not initialized", __func__);
00431 
00432         buffer_init(&buffer);
00433         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
00434             (const void *)&sshpam_conv);
00435         if (sshpam_err != PAM_SUCCESS)
00436                 goto auth_fail;
00437         sshpam_err = pam_authenticate(sshpam_handle, flags);
00438         if (sshpam_err != PAM_SUCCESS)
00439                 goto auth_fail;
00440 
00441         if (compat20) {
00442                 if (!do_pam_account())
00443                         goto auth_fail;
00444                 if (sshpam_authctxt->force_pwchange) {
00445                         sshpam_err = pam_chauthtok(sshpam_handle,
00446                             PAM_CHANGE_EXPIRED_AUTHTOK);
00447                         if (sshpam_err != PAM_SUCCESS)
00448                                 goto auth_fail;
00449                         sshpam_password_change_required(0);
00450                 }
00451         }
00452 
00453         buffer_put_cstring(&buffer, "OK");
00454 
00455 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
00456         /* Export variables set by do_pam_account */
00457         buffer_put_int(&buffer, sshpam_account_status);
00458         buffer_put_int(&buffer, sshpam_authctxt->force_pwchange);
00459 
00460         /* Export any environment strings set in child */
00461         for(i = 0; environ[i] != NULL; i++)
00462                 ; /* Count */
00463         buffer_put_int(&buffer, i);
00464         for(i = 0; environ[i] != NULL; i++)
00465                 buffer_put_cstring(&buffer, environ[i]);
00466 
00467         /* Export any environment strings set by PAM in child */
00468         env_from_pam = pam_getenvlist(sshpam_handle);
00469         for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
00470                 ; /* Count */
00471         buffer_put_int(&buffer, i);
00472         for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
00473                 buffer_put_cstring(&buffer, env_from_pam[i]);
00474 #endif /* UNSUPPORTED_POSIX_THREADS_HACK */
00475 
00476         /* XXX - can't do much about an error here */
00477         ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);
00478         buffer_free(&buffer);
00479         pthread_exit(NULL);
00480 
00481  auth_fail:
00482         buffer_put_cstring(&buffer,
00483             pam_strerror(sshpam_handle, sshpam_err));
00484         /* XXX - can't do much about an error here */
00485         ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
00486         buffer_free(&buffer);
00487         pthread_exit(NULL);
00488 
00489         return (NULL); /* Avoid warning for non-pthread case */
00490 }
00491 
00492 void
00493 sshpam_thread_cleanup(void)
00494 {
00495         struct pam_ctxt *ctxt = cleanup_ctxt;
00496 
00497         debug3("PAM: %s entering", __func__);
00498         if (ctxt != NULL && ctxt->pam_thread != 0) {
00499                 pthread_cancel(ctxt->pam_thread);
00500                 pthread_join(ctxt->pam_thread, NULL);
00501                 close(ctxt->pam_psock);
00502                 close(ctxt->pam_csock);
00503                 memset(ctxt, 0, sizeof(*ctxt));
00504                 cleanup_ctxt = NULL;
00505         }
00506 }
00507 
00508 static int
00509 sshpam_null_conv(int n, sshpam_const struct pam_message **msg,
00510     struct pam_response **resp, void *data)
00511 {
00512         debug3("PAM: %s entering, %d messages", __func__, n);
00513         return (PAM_CONV_ERR);
00514 }
00515 
00516 static struct pam_conv null_conv = { sshpam_null_conv, NULL };
00517 
00518 static int
00519 sshpam_store_conv(int n, sshpam_const struct pam_message **msg,
00520     struct pam_response **resp, void *data)
00521 {
00522         struct pam_response *reply;
00523         int i;
00524         size_t len;
00525 
00526         debug3("PAM: %s called with %d messages", __func__, n);
00527         *resp = NULL;
00528 
00529         if (n <= 0 || n > PAM_MAX_NUM_MSG)
00530                 return (PAM_CONV_ERR);
00531 
00532         if ((reply = malloc(n * sizeof(*reply))) == NULL)
00533                 return (PAM_CONV_ERR);
00534         memset(reply, 0, n * sizeof(*reply));
00535 
00536         for (i = 0; i < n; ++i) {
00537                 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
00538                 case PAM_ERROR_MSG:
00539                 case PAM_TEXT_INFO:
00540                         len = strlen(PAM_MSG_MEMBER(msg, i, msg));
00541                         buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len);
00542                         buffer_append(&loginmsg, "\n", 1 );
00543                         reply[i].resp_retcode = PAM_SUCCESS;
00544                         break;
00545                 default:
00546                         goto fail;
00547                 }
00548         }
00549         *resp = reply;
00550         return (PAM_SUCCESS);
00551 
00552  fail:
00553         for(i = 0; i < n; i++) {
00554                 if (reply[i].resp != NULL)
00555                         xfree(reply[i].resp);
00556         }
00557         xfree(reply);
00558         return (PAM_CONV_ERR);
00559 }
00560 
00561 static struct pam_conv store_conv = { sshpam_store_conv, NULL };
00562 
00563 void
00564 sshpam_cleanup(void)
00565 {
00566         debug("PAM: cleanup");
00567         if (sshpam_handle == NULL)
00568                 return;
00569         pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
00570         if (sshpam_cred_established) {
00571                 pam_setcred(sshpam_handle, PAM_DELETE_CRED);
00572                 sshpam_cred_established = 0;
00573         }
00574         if (sshpam_session_open) {
00575                 pam_close_session(sshpam_handle, PAM_SILENT);
00576                 sshpam_session_open = 0;
00577         }
00578         sshpam_authenticated = 0;
00579         pam_end(sshpam_handle, sshpam_err);
00580         sshpam_handle = NULL;
00581 }
00582 
00583 static int
00584 sshpam_init(Authctxt *authctxt)
00585 {
00586         extern char *__progname;
00587         const char *pam_rhost, *pam_user, *user = authctxt->user;
00588         const char **ptr_pam_user = &pam_user;
00589 
00590         if (sshpam_handle != NULL) {
00591                 /* We already have a PAM context; check if the user matches */
00592                 sshpam_err = pam_get_item(sshpam_handle,
00593                     PAM_USER, (sshpam_const void **)ptr_pam_user);
00594                 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
00595                         return (0);
00596                 pam_end(sshpam_handle, sshpam_err);
00597                 sshpam_handle = NULL;
00598         }
00599         debug("PAM: initializing for \"%s\"", user);
00600         sshpam_err =
00601             pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle);
00602         sshpam_authctxt = authctxt;
00603 
00604         if (sshpam_err != PAM_SUCCESS) {
00605                 pam_end(sshpam_handle, sshpam_err);
00606                 sshpam_handle = NULL;
00607                 return (-1);
00608         }
00609         pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns);
00610         debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost);
00611         sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost);
00612         if (sshpam_err != PAM_SUCCESS) {
00613                 pam_end(sshpam_handle, sshpam_err);
00614                 sshpam_handle = NULL;
00615                 return (-1);
00616         }
00617 #ifdef PAM_TTY_KLUDGE
00618         /*
00619          * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
00620          * sshd doesn't set the tty until too late in the auth process and
00621          * may not even set one (for tty-less connections)
00622          */
00623         debug("PAM: setting PAM_TTY to \"ssh\"");
00624         sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh");
00625         if (sshpam_err != PAM_SUCCESS) {
00626                 pam_end(sshpam_handle, sshpam_err);
00627                 sshpam_handle = NULL;
00628                 return (-1);
00629         }
00630 #endif
00631         return (0);
00632 }
00633 
00634 static void *
00635 sshpam_init_ctx(Authctxt *authctxt)
00636 {
00637         struct pam_ctxt *ctxt;
00638         int socks[2];
00639 
00640         debug3("PAM: %s entering", __func__);
00641         /* Refuse to start if we don't have PAM enabled */
00642         if (!options.use_pam)
00643                 return NULL;
00644 
00645         /* Initialize PAM */
00646         if (sshpam_init(authctxt) == -1) {
00647                 error("PAM: initialization failed");
00648                 return (NULL);
00649         }
00650 
00651         ctxt = xmalloc(sizeof *ctxt);
00652         memset(ctxt, 0, sizeof(*ctxt));
00653 
00654         /* Start the authentication thread */
00655         if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
00656                 error("PAM: failed create sockets: %s", strerror(errno));
00657                 xfree(ctxt);
00658                 return (NULL);
00659         }
00660         ctxt->pam_psock = socks[0];
00661         ctxt->pam_csock = socks[1];
00662         if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) {
00663                 error("PAM: failed to start authentication thread: %s",
00664                     strerror(errno));
00665                 close(socks[0]);
00666                 close(socks[1]);
00667                 xfree(ctxt);
00668                 return (NULL);
00669         }
00670         cleanup_ctxt = ctxt;
00671         return (ctxt);
00672 }
00673 
00674 static int
00675 sshpam_query(void *ctx, char **name, char **info,
00676     u_int *num, char ***prompts, u_int **echo_on)
00677 {
00678         Buffer buffer;
00679         struct pam_ctxt *ctxt = ctx;
00680         size_t plen;
00681         u_char type;
00682         char *msg;
00683         size_t len, mlen;
00684 
00685         debug3("PAM: %s entering", __func__);
00686         buffer_init(&buffer);
00687         *name = xstrdup("");
00688         *info = xstrdup("");
00689         *prompts = xmalloc(sizeof(char *));
00690         **prompts = NULL;
00691         plen = 0;
00692         *echo_on = xmalloc(sizeof(u_int));
00693         while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
00694                 type = buffer_get_char(&buffer);
00695                 msg = buffer_get_string(&buffer, NULL);
00696                 mlen = strlen(msg);
00697                 switch (type) {
00698                 case PAM_PROMPT_ECHO_ON:
00699                 case PAM_PROMPT_ECHO_OFF:
00700                         *num = 1;
00701                         len = plen + mlen + 1;
00702                         **prompts = xrealloc(**prompts, len);
00703                         strlcpy(**prompts + plen, msg, len - plen);
00704                         plen += mlen;
00705                         **echo_on = (type == PAM_PROMPT_ECHO_ON);
00706                         xfree(msg);
00707                         return (0);
00708                 case PAM_ERROR_MSG:
00709                 case PAM_TEXT_INFO:
00710                         /* accumulate messages */
00711                         len = plen + mlen + 2;
00712                         **prompts = xrealloc(**prompts, len);
00713                         strlcpy(**prompts + plen, msg, len - plen);
00714                         plen += mlen;
00715                         strlcat(**prompts + plen, "\n", len - plen);
00716                         plen++;
00717                         xfree(msg);
00718                         break;
00719                 case PAM_AUTH_ERR:
00720                         debug3("PAM: PAM_AUTH_ERR");
00721                         if (**prompts != NULL && strlen(**prompts) != 0) {
00722                                 *info = **prompts;
00723                                 **prompts = NULL;
00724                                 *num = 0;
00725                                 **echo_on = 0;
00726                                 ctxt->pam_done = -1;
00727                                 return 0;
00728                         }
00729                         /* FALLTHROUGH */
00730                 case PAM_SUCCESS:
00731                         if (**prompts != NULL) {
00732                                 /* drain any accumulated messages */
00733                                 debug("PAM: %s", **prompts);
00734                                 buffer_append(&loginmsg, **prompts,
00735                                     strlen(**prompts));
00736                                 xfree(**prompts);
00737                                 **prompts = NULL;
00738                         }
00739                         if (type == PAM_SUCCESS) {
00740                                 if (!sshpam_authctxt->valid ||
00741                                     (sshpam_authctxt->pw->pw_uid == 0 &&
00742                                     options.permit_root_login != PERMIT_YES))
00743                                         fatal("Internal error: PAM auth "
00744                                             "succeeded when it should have "
00745                                             "failed");
00746                                 import_environments(&buffer);
00747                                 *num = 0;
00748                                 **echo_on = 0;
00749                                 ctxt->pam_done = 1;
00750                                 xfree(msg);
00751                                 return (0);
00752                         }
00753                         error("PAM: %s for %s%.100s from %.100s", msg,
00754                             sshpam_authctxt->valid ? "" : "illegal user ",
00755                             sshpam_authctxt->user,
00756                             get_remote_name_or_ip(utmp_len, options.use_dns));
00757                         /* FALLTHROUGH */
00758                 default:
00759                         *num = 0;
00760                         **echo_on = 0;
00761                         xfree(msg);
00762                         ctxt->pam_done = -1;
00763                         return (-1);
00764                 }
00765         }
00766         return (-1);
00767 }
00768 
00769 /* XXX - see also comment in auth-chall.c:verify_response */
00770 static int
00771 sshpam_respond(void *ctx, u_int num, char **resp)
00772 {
00773         Buffer buffer;
00774         struct pam_ctxt *ctxt = ctx;
00775 
00776         debug2("PAM: %s entering, %u responses", __func__, num);
00777         switch (ctxt->pam_done) {
00778         case 1:
00779                 sshpam_authenticated = 1;
00780                 return (0);
00781         case 0:
00782                 break;
00783         default:
00784                 return (-1);
00785         }
00786         if (num != 1) {
00787                 error("PAM: expected one response, got %u", num);
00788                 return (-1);
00789         }
00790         buffer_init(&buffer);
00791         if (sshpam_authctxt->valid &&
00792             (sshpam_authctxt->pw->pw_uid != 0 ||
00793             options.permit_root_login == PERMIT_YES))
00794                 buffer_put_cstring(&buffer, *resp);
00795         else
00796                 buffer_put_cstring(&buffer, badpw);
00797         if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) {
00798                 buffer_free(&buffer);
00799                 return (-1);
00800         }
00801         buffer_free(&buffer);
00802         return (1);
00803 }
00804 
00805 static void
00806 sshpam_free_ctx(void *ctxtp)
00807 {
00808         struct pam_ctxt *ctxt = ctxtp;
00809 
00810         debug3("PAM: %s entering", __func__);
00811         sshpam_thread_cleanup();
00812         xfree(ctxt);
00813         /*
00814          * We don't call sshpam_cleanup() here because we may need the PAM
00815          * handle at a later stage, e.g. when setting up a session.  It's
00816          * still on the cleanup list, so pam_end() *will* be called before
00817          * the server process terminates.
00818          */
00819 }
00820 
00821 KbdintDevice sshpam_device = {
00822         "pam",
00823         sshpam_init_ctx,
00824         sshpam_query,
00825         sshpam_respond,
00826         sshpam_free_ctx
00827 };
00828 
00829 KbdintDevice mm_sshpam_device = {
00830         "pam",
00831         mm_sshpam_init_ctx,
00832         mm_sshpam_query,
00833         mm_sshpam_respond,
00834         mm_sshpam_free_ctx
00835 };
00836 
00837 /*
00838  * This replaces auth-pam.c
00839  */
00840 void
00841 start_pam(Authctxt *authctxt)
00842 {
00843         if (!options.use_pam)
00844                 fatal("PAM: initialisation requested when UsePAM=no");
00845 
00846         if (sshpam_init(authctxt) == -1)
00847                 fatal("PAM: initialisation failed");
00848 }
00849 
00850 void
00851 finish_pam(void)
00852 {
00853         sshpam_cleanup();
00854 }
00855 
00856 u_int
00857 do_pam_account(void)
00858 {
00859         debug("%s: called", __func__);
00860         if (sshpam_account_status != -1)
00861                 return (sshpam_account_status);
00862 
00863         sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
00864         debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err,
00865             pam_strerror(sshpam_handle, sshpam_err));
00866 
00867         if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) {
00868                 sshpam_account_status = 0;
00869                 return (sshpam_account_status);
00870         }
00871 
00872         if (sshpam_err == PAM_NEW_AUTHTOK_REQD)
00873                 sshpam_password_change_required(1);
00874 
00875         sshpam_account_status = 1;
00876         return (sshpam_account_status);
00877 }
00878 
00879 void
00880 do_pam_set_tty(const char *tty)
00881 {
00882         if (tty != NULL) {
00883                 debug("PAM: setting PAM_TTY to \"%s\"", tty);
00884                 sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty);
00885                 if (sshpam_err != PAM_SUCCESS)
00886                         fatal("PAM: failed to set PAM_TTY: %s",
00887                             pam_strerror(sshpam_handle, sshpam_err));
00888         }
00889 }
00890 
00891 void
00892 do_pam_setcred(int init)
00893 {
00894         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
00895             (const void *)&store_conv);
00896         if (sshpam_err != PAM_SUCCESS)
00897                 fatal("PAM: failed to set PAM_CONV: %s",
00898                     pam_strerror(sshpam_handle, sshpam_err));
00899         if (init) {
00900                 debug("PAM: establishing credentials");
00901                 sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
00902         } else {
00903                 debug("PAM: reinitializing credentials");
00904                 sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
00905         }
00906         if (sshpam_err == PAM_SUCCESS) {
00907                 sshpam_cred_established = 1;
00908                 return;
00909         }
00910         if (sshpam_authenticated)
00911                 fatal("PAM: pam_setcred(): %s",
00912                     pam_strerror(sshpam_handle, sshpam_err));
00913         else
00914                 debug("PAM: pam_setcred(): %s",
00915                     pam_strerror(sshpam_handle, sshpam_err));
00916 }
00917 
00918 static int
00919 sshpam_tty_conv(int n, sshpam_const struct pam_message **msg,
00920     struct pam_response **resp, void *data)
00921 {
00922         char input[PAM_MAX_MSG_SIZE];
00923         struct pam_response *reply;
00924         int i;
00925 
00926         debug3("PAM: %s called with %d messages", __func__, n);
00927 
00928         *resp = NULL;
00929 
00930         if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO))
00931                 return (PAM_CONV_ERR);
00932 
00933         if ((reply = malloc(n * sizeof(*reply))) == NULL)
00934                 return (PAM_CONV_ERR);
00935         memset(reply, 0, n * sizeof(*reply));
00936 
00937         for (i = 0; i < n; ++i) {
00938                 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
00939                 case PAM_PROMPT_ECHO_OFF:
00940                         reply[i].resp =
00941                             read_passphrase(PAM_MSG_MEMBER(msg, i, msg),
00942                             RP_ALLOW_STDIN);
00943                         reply[i].resp_retcode = PAM_SUCCESS;
00944                         break;
00945                 case PAM_PROMPT_ECHO_ON:
00946                         fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
00947                         fgets(input, sizeof input, stdin);
00948                         if ((reply[i].resp = strdup(input)) == NULL)
00949                                 goto fail;
00950                         reply[i].resp_retcode = PAM_SUCCESS;
00951                         break;
00952                 case PAM_ERROR_MSG:
00953                 case PAM_TEXT_INFO:
00954                         fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
00955                         reply[i].resp_retcode = PAM_SUCCESS;
00956                         break;
00957                 default:
00958                         goto fail;
00959                 }
00960         }
00961         *resp = reply;
00962         return (PAM_SUCCESS);
00963 
00964  fail:
00965         for(i = 0; i < n; i++) {
00966                 if (reply[i].resp != NULL)
00967                         xfree(reply[i].resp);
00968         }
00969         xfree(reply);
00970         return (PAM_CONV_ERR);
00971 }
00972 
00973 static struct pam_conv tty_conv = { sshpam_tty_conv, NULL };
00974 
00975 /*
00976  * XXX this should be done in the authentication phase, but ssh1 doesn't
00977  * support that
00978  */
00979 void
00980 do_pam_chauthtok(void)
00981 {
00982         if (use_privsep)
00983                 fatal("Password expired (unable to change with privsep)");
00984         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
00985             (const void *)&tty_conv);
00986         if (sshpam_err != PAM_SUCCESS)
00987                 fatal("PAM: failed to set PAM_CONV: %s",
00988                     pam_strerror(sshpam_handle, sshpam_err));
00989         debug("PAM: changing password");
00990         sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
00991         if (sshpam_err != PAM_SUCCESS)
00992                 fatal("PAM: pam_chauthtok(): %s",
00993                     pam_strerror(sshpam_handle, sshpam_err));
00994 }
00995 
00996 void
00997 do_pam_session(void)
00998 {
00999         debug3("PAM: opening session");
01000         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
01001             (const void *)&store_conv);
01002         if (sshpam_err != PAM_SUCCESS)
01003                 fatal("PAM: failed to set PAM_CONV: %s",
01004                     pam_strerror(sshpam_handle, sshpam_err));
01005         sshpam_err = pam_open_session(sshpam_handle, 0);
01006         if (sshpam_err == PAM_SUCCESS)
01007                 sshpam_session_open = 1;
01008         else {
01009                 sshpam_session_open = 0;
01010                 disable_forwarding();
01011                 error("PAM: pam_open_session(): %s",
01012                     pam_strerror(sshpam_handle, sshpam_err));
01013         }
01014 
01015 }
01016 
01017 int
01018 is_pam_session_open(void)
01019 {
01020         return sshpam_session_open;
01021 }
01022 
01023 /*
01024  * Set a PAM environment string. We need to do this so that the session
01025  * modules can handle things like Kerberos/GSI credentials that appear
01026  * during the ssh authentication process.
01027  */
01028 int
01029 do_pam_putenv(char *name, char *value)
01030 {
01031         int ret = 1;
01032 #ifdef HAVE_PAM_PUTENV
01033         char *compound;
01034         size_t len;
01035 
01036         len = strlen(name) + strlen(value) + 2;
01037         compound = xmalloc(len);
01038 
01039         snprintf(compound, len, "%s=%s", name, value);
01040         ret = pam_putenv(sshpam_handle, compound);
01041         xfree(compound);
01042 #endif
01043 
01044         return (ret);
01045 }
01046 
01047 char **
01048 fetch_pam_child_environment(void)
01049 {
01050         return sshpam_env;
01051 }
01052 
01053 char **
01054 fetch_pam_environment(void)
01055 {
01056         return (pam_getenvlist(sshpam_handle));
01057 }
01058 
01059 void
01060 free_pam_environment(char **env)
01061 {
01062         char **envp;
01063 
01064         if (env == NULL)
01065                 return;
01066 
01067         for (envp = env; *envp; envp++)
01068                 xfree(*envp);
01069         xfree(env);
01070 }
01071 
01072 /*
01073  * "Blind" conversation function for password authentication.  Assumes that
01074  * echo-off prompts are for the password and stores messages for later
01075  * display.
01076  */
01077 static int
01078 sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg,
01079     struct pam_response **resp, void *data)
01080 {
01081         struct pam_response *reply;
01082         int i;
01083         size_t len;
01084 
01085         debug3("PAM: %s called with %d messages", __func__, n);
01086 
01087         *resp = NULL;
01088 
01089         if (n <= 0 || n > PAM_MAX_NUM_MSG)
01090                 return (PAM_CONV_ERR);
01091 
01092         if ((reply = malloc(n * sizeof(*reply))) == NULL)
01093                 return (PAM_CONV_ERR);
01094         memset(reply, 0, n * sizeof(*reply));
01095 
01096         for (i = 0; i < n; ++i) {
01097                 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
01098                 case PAM_PROMPT_ECHO_OFF:
01099                         if (sshpam_password == NULL)
01100                                 goto fail;
01101                         if ((reply[i].resp = strdup(sshpam_password)) == NULL)
01102                                 goto fail;
01103                         reply[i].resp_retcode = PAM_SUCCESS;
01104                         break;
01105                 case PAM_ERROR_MSG:
01106                 case PAM_TEXT_INFO:
01107                         len = strlen(PAM_MSG_MEMBER(msg, i, msg));
01108                         if (len > 0) {
01109                                 buffer_append(&loginmsg,
01110                                     PAM_MSG_MEMBER(msg, i, msg), len);
01111                                 buffer_append(&loginmsg, "\n", 1);
01112                         }
01113                         if ((reply[i].resp = strdup("")) == NULL)
01114                                 goto fail;
01115                         reply[i].resp_retcode = PAM_SUCCESS;
01116                         break;
01117                 default:
01118                         goto fail;
01119                 }
01120         }
01121         *resp = reply;
01122         return (PAM_SUCCESS);
01123 
01124  fail:
01125         for(i = 0; i < n; i++) {
01126                 if (reply[i].resp != NULL)
01127                         xfree(reply[i].resp);
01128         }
01129         xfree(reply);
01130         return (PAM_CONV_ERR);
01131 }
01132 
01133 static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL };
01134 
01135 /*
01136  * Attempt password authentication via PAM
01137  */
01138 int
01139 sshpam_auth_passwd(Authctxt *authctxt, const char *password)
01140 {
01141         int flags = (options.permit_empty_passwd == 0 ?
01142             PAM_DISALLOW_NULL_AUTHTOK : 0);
01143 
01144         if (!options.use_pam || sshpam_handle == NULL)
01145                 fatal("PAM: %s called when PAM disabled or failed to "
01146                     "initialise.", __func__);
01147 
01148         sshpam_password = password;
01149         sshpam_authctxt = authctxt;
01150 
01151         /*
01152          * If the user logging in is invalid, or is root but is not permitted
01153          * by PermitRootLogin, use an invalid password to prevent leaking
01154          * information via timing (eg if the PAM config has a delay on fail).
01155          */
01156         if (!authctxt->valid || (authctxt->pw->pw_uid == 0 &&
01157             options.permit_root_login != PERMIT_YES))
01158                 sshpam_password = badpw;
01159 
01160         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
01161             (const void *)&passwd_conv);
01162         if (sshpam_err != PAM_SUCCESS)
01163                 fatal("PAM: %s: failed to set PAM_CONV: %s", __func__,
01164                     pam_strerror(sshpam_handle, sshpam_err));
01165 
01166         sshpam_err = pam_authenticate(sshpam_handle, flags);
01167         sshpam_password = NULL;
01168         if (sshpam_err == PAM_SUCCESS && authctxt->valid) {
01169                 debug("PAM: password authentication accepted for %.100s",
01170                     authctxt->user);
01171                 return 1;
01172         } else {
01173                 debug("PAM: password authentication failed for %.100s: %s",
01174                     authctxt->valid ? authctxt->user : "an illegal user",
01175                     pam_strerror(sshpam_handle, sshpam_err));
01176                 return 0;
01177         }
01178 }
01179 #endif /* USE_PAM */

© sourcejam.com 2005-2008