summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNalin Dahyabhai <nalin@fedoraproject.org>2007-06-22 22:03:14 +0000
committerNalin Dahyabhai <nalin@fedoraproject.org>2007-06-22 22:03:14 +0000
commit547fdc81dba6437fc0140be7bc7fac3fd43aecbe (patch)
treee94176a839590335f3d13f7d432518797d5cdc0b
parent3f47a21b9d93d595212007c1912a1b850983d1b7 (diff)
- PAM support for rshd, login (used by telnet and rlogind), and ftpd
-rw-r--r--krb5-1.6.1-pam.patch1017
1 files changed, 1017 insertions, 0 deletions
diff --git a/krb5-1.6.1-pam.patch b/krb5-1.6.1-pam.patch
new file mode 100644
index 0000000..a753616
--- /dev/null
+++ b/krb5-1.6.1-pam.patch
@@ -0,0 +1,1017 @@
+Modify krshd so that it performs PAM account and session management. It
+must now always fork so that it can always clean up the session. The
+PAM session is opened and credentials initialized after any forwarded
+credentials are stored to disk and before access to the user's home
+directory is attempted. The default service name is "kshell" or
+"ekshell", depending on whether or not encryption is in use, to avoid a
+dependency or conflict on the plain rsh server's configuration file. At
+run-time, krshd's behavior can be reset to the earlier, non-PAM behavior
+by setting "use_pam" to false in the [rshd] section of /etc/krb5.conf.
+
+Modify ftpd so that authentication with a plaintext password goes
+through PAM, and it performs PAM account and session management. The
+PAM session is opened and credentials initialized after any forwarded
+credentials are stored to disk. The default service name is "gssftp",
+mainly to avoid conflicts with other FTP servers' configuration files.
+At run-time, krshd's behavior can be reset to the earlier, non-PAM
+behavior by setting "use_pam" to false in the [ftpd] section of
+/etc/krb5.conf.
+
+Modify login so that instead of directly obtaining v5 or v4 credentials
+or running aklog, it calls PAM for authentication if strong
+authentication hasn't already been performed, so that it performs
+account management using PAM (prompting for a password change if need
+be), and that it performs session management. The PAM session is opened
+and credentials initialized after any forwarded credentials are stored
+to disk. The default service name is "login", because its configuration
+is pretty much always going to be there. At run-time, login's behavior
+can be reset to the earlier, non-PAM behavior by setting "use_pam" to
+false in the [login] section of /etc/krb5.conf.
+
+When enabled, ftpd, krshd, and login.krb5 gain dependence on libpam.
+
+--- krb5-1.6.1/src/appl/bsd/configure.in 2006-03-27 23:35:02.000000000 -0500
++++ krb5-1.6.1/src/appl/bsd/configure.in 2007-06-21 17:39:57.000000000 -0400
+@@ -24,6 +24,7 @@ AC_CHECK_LIB(odm,main,
+ AC_CHECK_LIB(cfg,main,
+ LOGINLIBS="$LOGINLIBS -lodm -ls -lcfg"
+ )))
++KRB5_WITH_PAM
+ dnl
+ dnl Make our operating system-specific security checks and definitions for
+ dnl login.
+--- krb5-1.6.1/src/appl/bsd/krshd.c 2006-10-15 03:50:16.000000000 -0400
++++ krb5-1.6.1/src/appl/bsd/krshd.c 2007-06-22 14:28:57.000000000 -0400
+@@ -185,6 +185,10 @@ Key_schedule v4_schedule;
+ #include <arpa/nameser.h>
+ #endif
+
++#ifdef USE_PAM
++#include "pam.h"
++#endif
++
+ #ifndef MAXDNAME
+ #define MAXDNAME 256 /*per the rfc*/
+ #endif
+@@ -205,6 +209,7 @@ void fatal(int, const char *);
+
+ int require_encrypt = 0;
+ int do_encrypt = 0;
++int force_fork = 0;
+ int anyport = 0;
+ char *kprogdir = KPROGDIR;
+ int netf;
+@@ -1085,14 +1090,6 @@ void doit(f, fromp)
+ }
+ #endif /*CRAY*/
+
+- if (chdir(pwd->pw_dir) < 0) {
+- if(chdir("/") < 0) {
+- error("No remote directory.\n");
+- goto signout_please;
+- }
+- pwd->pw_dir = "/";
+- }
+-
+ #ifdef KERBEROS
+
+ #if defined(KRB5_KRB4_COMPAT) && !defined(ALWAYS_V5_KUSEROK)
+@@ -1151,11 +1148,49 @@ void doit(f, fromp)
+ goto signout_please;
+ }
+
++#ifdef USE_PAM
++ if (appl_pam_enabled(bsd_context, "rshd")) {
++ if (appl_pam_acct_mgmt(do_encrypt ?
++ EKSHELL_PAM_SERVICE :
++ KSHELL_PAM_SERVICE,
++ 0,
++ locuser,
++ NULL,
++ do_encrypt ?
++ EKSHELL_PAM_SERVICE :
++ KSHELL_PAM_SERVICE) != 0) {
++ error("Login denied.\n");
++ goto signout_please;
++ }
++ if (appl_pam_requires_chauthtok()) {
++ error("Password change required, but not possible over rsh.\n");
++ goto signout_please;
++ }
++ force_fork = 1;
++ appl_pam_set_forwarded_ccname(getenv("KRB5CCNAME"));
++ if (appl_pam_session_open() != 0) {
++ error("Login failure.\n");
++ goto signout_please;
++ }
++ if (appl_pam_cred_init()) {
++ error("Login failure.\n");
++ goto signout_please;
++ }
++ } else
++#endif
+ if (pwd->pw_uid && !access(NOLOGIN, F_OK)) {
+ error("Logins currently disabled.\n");
+ goto signout_please;
+ }
+
++ if (chdir(pwd->pw_dir) < 0) {
++ if (chdir("/") < 0) {
++ error("No remote directory.\n");
++ goto signout_please;
++ }
++ pwd->pw_dir = "/";
++ }
++
+ /* Log access to account */
+ pwd = (struct passwd *) getpwnam(locuser);
+ if (pwd && (pwd->pw_uid == 0)) {
+@@ -1195,7 +1230,7 @@ void doit(f, fromp)
+
+ (void) write(2, "", 1);
+
+- if (port||do_encrypt) {
++ if (port||do_encrypt||force_fork) {
+ if (port&&(pipe(pv) < 0)) {
+ error("Can't make pipe.\n");
+ goto signout_please;
+@@ -1507,6 +1542,15 @@ void doit(f, fromp)
+
+ environ = envinit;
+
++#ifdef USE_PAM
++ if (appl_pam_enabled(bsd_context, "rshd")) {
++ if (appl_pam_setenv() != 0) {
++ error("Login failure.\n");
++ goto signout_please;
++ }
++ }
++#endif
++
+ #ifdef KERBEROS
+ /* To make Kerberos rcp work correctly, we must ensure that we
+ invoke Kerberos rcp on this end, not normal rcp, even if the
+--- krb5-1.6.1/src/appl/bsd/Makefile.in 2006-10-06 17:17:56.000000000 -0400
++++ krb5-1.6.1/src/appl/bsd/Makefile.in 2007-06-21 17:39:57.000000000 -0400
+@@ -14,13 +14,14 @@ LIBOBJS=@LIBOBJS@
+ V4RCP=@V4RCP@
+ V4RCPO=@V4RCPO@
+ KRSHDLIBS=@KRSHDLIBS@
++PAMOBJS=pam.o
+
+ SRCS= $(srcdir)/krcp.c $(srcdir)/krlogin.c $(srcdir)/krsh.c $(srcdir)/kcmd.c \
+ $(srcdir)/forward.c $(srcdir)/compat_recv.c \
+ $(srcdir)/login.c $(srcdir)/krshd.c $(srcdir)/krlogind.c \
+ $(srcdir)/v4rcp.c
+ OBJS= krcp.o krlogin.o krsh.o kcmd.o forward.o compat_recv.o $(SETENVOBJ) \
+- login.o krshd.o krlogind.o $(V4RCPO) $(LIBOBJS)
++ login.o krshd.o krlogind.o $(V4RCPO) $(LIBOBJS) $(PAMOBJS)
+
+ UCB_RLOGIN = @UCB_RLOGIN@
+ UCB_RSH = @UCB_RSH@
+@@ -66,8 +67,8 @@ install::
+ ${DESTDIR}$(CLIENT_MANDIR)/`echo $$f|sed '$(transform)'`.1; \
+ fi
+
+-kshd: krshd.o kcmd.o forward.o compat_recv.o $(SETENVOBJ) $(LIBOBJS) $(PTY_DEPLIB) $(KRB4COMPAT_DEPLIBS) $(APPUTILS_DEPLIB)
+- $(CC_LINK) -o kshd krshd.o kcmd.o forward.o compat_recv.o $(SETENVOBJ) $(LIBOBJS) $(KRSHDLIBS) $(PTY_LIB) $(UTIL_LIB) $(KRB4COMPAT_LIBS) $(APPUTILS_LIB)
++kshd: krshd.o kcmd.o forward.o compat_recv.o $(SETENVOBJ) $(PAMOBJS) $(LIBOBJS) $(PTY_DEPLIB) $(KRB4COMPAT_DEPLIBS) $(APPUTILS_DEPLIB)
++ $(CC_LINK) -o kshd krshd.o kcmd.o forward.o compat_recv.o $(PAMOBJS) $(SETENVOBJ) $(LIBOBJS) $(KRSHDLIBS) $(PTY_LIB) $(UTIL_LIB) $(KRB4COMPAT_LIBS) $(PAM_LIBS) $(APPUTILS_LIB)
+
+ klogind: krlogind.o kcmd.o forward.o compat_recv.o $(SETENVOBJ) $(LIBOBJS) $(PTY_DEPLIB) $(KRB4COMPAT_DEPLIBS) $(APPUTILS_DEPLIB)
+ $(CC_LINK) -o klogind krlogind.o kcmd.o forward.o compat_recv.o $(SETENVOBJ) $(LIBOBJS) $(PTY_LIB) $(UTIL_LIB) $(KRB4COMPAT_LIBS) $(APPUTILS_LIB)
+@@ -84,8 +85,8 @@ install::
+ # No program name transformation is done with login.krb5 since it is directly
+ # referenced by klogind.
+ #
+-login.krb5: login.o $(SETENVOBJ) $(LIBOBJS) $(PTY_DEPLIB) $(KRB4COMPAT_DEPLIBS)
+- $(CC_LINK) -o login.krb5 login.o $(SETENVOBJ) $(LIBOBJS) $(LOGINLIBS) $(PTY_LIB) $(KRB4COMPAT_LIBS)
++login.krb5: login.o $(SETENVOBJ) $(PAMOBJS) $(LIBOBJS) $(PTY_DEPLIB) $(KRB4COMPAT_DEPLIBS)
++ $(CC_LINK) -o login.krb5 login.o $(SETENVOBJ) $(PAMOBJS) $(LIBOBJS) $(LOGINLIBS) $(PTY_LIB) $(KRB4COMPAT_LIBS) $(PAM_LIBS)
+
+ install::
+ $(INSTALL_PROGRAM) login.krb5 $(DESTDIR)$(SERVER_BINDIR)/login.krb5
+--- krb5-1.6.1/src/appl/bsd/login.c 2006-08-08 15:26:40.000000000 -0400
++++ krb5-1.6.1/src/appl/bsd/login.c 2007-06-22 14:09:41.000000000 -0400
+@@ -159,6 +159,11 @@ typedef sigtype (*handler)();
+ #include "osconf.h"
+ #endif /* KRB5_GET_TICKETS */
+
++#ifdef USE_PAM
++#include "pam.h"
++int login_use_pam = 1;
++#endif
++
+ #ifdef KRB4_KLOGIN
+ /* support for running under v4 klogind, -k -K flags */
+ #define KRB4
+@@ -351,6 +356,9 @@ static struct login_confs {
+ char *flagname;
+ int *flag;
+ } login_conf_set[] = {
++#ifdef USE_PAM
++ {USE_PAM_CONFIGURATION_KEYWORD, &login_use_pam},
++#endif
+ #ifdef KRB5_GET_TICKETS
+ {"krb5_get_tickets", &login_krb5_get_tickets},
+ #endif
+@@ -1292,6 +1294,18 @@ int main(argc, argv)
+ if (!unix_needs_passwd())
+ break;
+
++#ifdef USE_PAM
++ if (login_use_pam) {
++ if (appl_pam_authenticate(LOGIN_PAM_SERVICE, 1, username, NULL,
++ ttyname(STDIN_FILENO)) == PAM_SUCCESS) {
++ break;
++ } else {
++ /* the goto target label is in a different nesting scope, but
++ * it's roughly where we want to land */
++ goto bad_login;
++ }
++ }
++#endif
+ /* we have several sets of code:
+ 1) get v5 tickets alone -DKRB5_GET_TICKETS
+ 2) get v4 tickets alone [** don't! only get them *with* v5 **]
+@@ -1406,6 +1420,24 @@ int main(argc, argv)
+ /* committed to login -- turn off timeout */
+ (void) alarm((u_int) 0);
+
++#ifdef USE_PAM
++ if (login_use_pam) {
++ if (appl_pam_acct_mgmt(LOGIN_PAM_SERVICE, 0, username, NULL,
++ ttyname(STDIN_FILENO)) != 0) {
++ printf("Login incorrect\n");
++ sleepexit(1);
++ }
++ if (appl_pam_requires_chauthtok()) {
++ if (appl_pam_chauthtok() != 0) {
++ printf("Failed to change password.\n");
++ sleepexit(1);
++ }
++ }
++ } else {
++ /* the "else" here is the non-PAM behavior which continues until the
++ * next ifdef USE_PAM block, as of this writing more or less
++ * duplicating the work of pam_securetty and an OQUOTA check */
++#endif
+ /*
+ * If valid so far and root is logging in, see if root logins on
+ * this terminal are permitted.
+@@ -1446,6 +1478,21 @@ int main(argc, argv)
+ sleepexit(0);
+ }
+ #endif
++#ifdef USE_PAM
++ }
++#endif /* USE_PAM */
++
++#ifdef USE_PAM
++ if (login_use_pam) {
++ appl_pam_set_forwarded_ccname(getenv("KRB5CCNAME"));
++ if (appl_pam_session_open() != 0) {
++ sleepexit(1);
++ }
++ if (appl_pam_cred_init() != 0) {
++ sleepexit(1);
++ }
++ }
++#endif /* USE_PAM */
+
+ if (chdir(pwd->pw_dir) < 0) {
+ printf("No directory %s!\n", pwd->pw_dir);
+@@ -1792,6 +1839,11 @@ int main(argc, argv)
+ }
+ #endif /* KRB5_GET_TICKETS */
+
++#ifdef USE_PAM
++ if (login_use_pam)
++ appl_pam_setenv();
++#endif
++
+ if (tty[sizeof("tty")-1] == 'd')
+ syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
+ if (pwd->pw_uid == 0)
+--- /dev/null 2007-06-22 10:29:46.741860805 -0400
++++ krb5-1.6.1/src/appl/bsd/pam.c 2007-06-22 14:22:10.000000000 -0400
+@@ -0,0 +1,408 @@
++/*
++ * src/appl/bsd/pam.c
++ *
++ * Copyright 2007 Red Hat, Inc.
++ *
++ * All Rights Reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * Redistributions of source code must retain the above copyright notice, this
++ * list of conditions and the following disclaimer.
++ *
++ * Redistributions in binary form must reproduce the above copyright notice,
++ * this list of conditions and the following disclaimer in the documentation
++ * and/or other materials provided with the distribution.
++ *
++ * Neither the name of Red Hat, Inc. nor the names of its contributors may be
++ * used to endorse or promote products derived from this software without
++ * specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
++ * POSSIBILITY OF SUCH DAMAGE.
++ *
++ * Convenience wrappers for using PAM.
++ */
++
++#ifdef USE_PAM
++#include <sys/types.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++#include "k5-int.h"
++#include "pam.h"
++
++#ifndef MAXPWSIZE
++#define MAXPWSIZE 128
++#endif
++
++static int appl_pam_started;
++static pid_t appl_pam_starter = -1;
++static int appl_pam_session_opened;
++static int appl_pam_creds_initialized;
++static int appl_pam_pwchange_required;
++static pam_handle_t *appl_pamh;
++static struct pam_conv appl_pam_conv;
++static char *appl_pam_user;
++struct appl_pam_non_interactive_args {
++ const char *user;
++ const char *password;
++};
++
++int
++appl_pam_enabled(krb5_context context, const char *section)
++{
++ int enabled = 1;
++ if (profile_get_boolean(context->profile,
++ section, USE_PAM_CONFIGURATION_KEYWORD, NULL,
++ enabled, &enabled) != 0) {
++ enabled = 1;
++ }
++ return enabled;
++}
++
++void
++appl_pam_cleanup(void)
++{
++ if (getpid() != appl_pam_starter) {
++ return;
++ }
++#ifdef DEBUG
++ printf("Called to clean up PAM.\n");
++#endif
++ if (appl_pam_creds_initialized) {
++#ifdef DEBUG
++ printf("Deleting PAM credentials.\n");
++#endif
++ pam_setcred(appl_pamh, PAM_DELETE_CRED);
++ appl_pam_creds_initialized = 0;
++ }
++ if (appl_pam_session_opened) {
++#ifdef DEBUG
++ printf("Closing PAM session.\n");
++#endif
++ pam_close_session(appl_pamh, 0);
++ appl_pam_session_opened = 0;
++ }
++ appl_pam_pwchange_required = 0;
++ if (appl_pam_started) {
++#ifdef DEBUG
++ printf("Shutting down PAM.\n");
++#endif
++ pam_end(appl_pamh, 0);
++ appl_pam_started = 0;
++ appl_pam_starter = -1;
++ free(appl_pam_user);
++ appl_pam_user = NULL;
++ }
++}
++static int
++appl_pam_interactive_converse(int num_msg, const struct pam_message **msg,
++ struct pam_response **presp, void *appdata_ptr)
++{
++ const struct pam_message *message;
++ struct pam_response *resp;
++ int i, code;
++ char *pwstring, pwbuf[MAXPWSIZE];
++ unsigned int pwsize;
++ resp = malloc(sizeof(struct pam_response) * num_msg);
++ if (resp == NULL) {
++ return PAM_BUF_ERR;
++ }
++ memset(resp, 0, sizeof(struct pam_response) * num_msg);
++ code = PAM_SUCCESS;
++ for (i = 0; i < num_msg; i++) {
++ message = &(msg[0][i]); /* XXX */
++ message = msg[i]; /* XXX */
++ pwstring = NULL;
++ switch (message->msg_style) {
++ case PAM_TEXT_INFO:
++ case PAM_ERROR_MSG:
++ printf("[%s]\n", message->msg ? message->msg : "");
++ fflush(stdout);
++ resp[i].resp = NULL;
++ resp[i].resp_retcode = PAM_SUCCESS;
++ break;
++ case PAM_PROMPT_ECHO_ON:
++ case PAM_PROMPT_ECHO_OFF:
++ if (message->msg_style == PAM_PROMPT_ECHO_ON) {
++ if (fgets(pwbuf, sizeof(pwbuf),
++ stdin) != NULL) {
++ pwbuf[strspn(pwbuf, "\r\n")] = '\0';
++ pwstring = pwbuf;
++ }
++ } else {
++ pwstring = getpass(message->msg ?
++ message->msg :
++ "");
++ }
++ if ((pwstring != NULL) && (pwstring[0] != '\0')) {
++ pwsize = strlen(pwstring);
++ resp[i].resp = malloc(pwsize + 1);
++ if (resp[i].resp == NULL) {
++ resp[i].resp_retcode = PAM_BUF_ERR;
++ } else {
++ memcpy(resp[i].resp, pwstring, pwsize);
++ resp[i].resp[pwsize] = '\0';
++ resp[i].resp_retcode = PAM_SUCCESS;
++ }
++ } else {
++ resp[i].resp_retcode = PAM_CONV_ERR;
++ code = PAM_CONV_ERR;
++ }
++ break;
++ default:
++ break;
++ }
++ }
++ *presp = resp;
++ return code;
++}
++static int
++appl_pam_non_interactive_converse(int num_msg,
++ const struct pam_message **msg,
++ struct pam_response **presp,
++ void *appdata_ptr)
++{
++ const struct pam_message *message;
++ struct pam_response *resp;
++ int i, code;
++ unsigned int pwsize;
++ struct appl_pam_non_interactive_args *args;
++ const char *pwstring;
++ resp = malloc(sizeof(struct pam_response) * num_msg);
++ if (resp == NULL) {
++ return PAM_BUF_ERR;
++ }
++ args = appdata_ptr;
++ memset(resp, 0, sizeof(struct pam_response) * num_msg);
++ code = PAM_SUCCESS;
++ for (i = 0; i < num_msg; i++) {
++ message = &((*msg)[i]);
++ message = msg[i];
++ pwstring = NULL;
++ switch (message->msg_style) {
++ case PAM_TEXT_INFO:
++ case PAM_ERROR_MSG:
++ break;
++ case PAM_PROMPT_ECHO_ON:
++ case PAM_PROMPT_ECHO_OFF:
++ if (message->msg_style == PAM_PROMPT_ECHO_ON) {
++ /* assume "user" */
++ pwstring = args->user;
++ } else {
++ /* assume "password" */
++ pwstring = args->password;
++ }
++ if ((pwstring != NULL) && (pwstring[0] != '\0')) {
++ pwsize = strlen(pwstring);
++ resp[i].resp = malloc(pwsize + 1);
++ if (resp[i].resp == NULL) {
++ resp[i].resp_retcode = PAM_BUF_ERR;
++ } else {
++ memcpy(resp[i].resp, pwstring, pwsize);
++ resp[i].resp[pwsize] = '\0';
++ resp[i].resp_retcode = PAM_SUCCESS;
++ }
++ } else {
++ resp[i].resp_retcode = PAM_CONV_ERR;
++ code = PAM_CONV_ERR;
++ }
++ break;
++ default:
++ break;
++ }
++ }
++ *presp = resp;
++ return code;
++}
++void
++appl_pam_set_forwarded_ccname(const char *ccname)
++{
++ char *ccname2;
++ if (appl_pam_started && (ccname != NULL) && (strlen(ccname) > 0)) {
++ ccname2 = malloc(strlen(KRB5_ENV_CCNAME) + strlen(ccname) + 2);
++ if (ccname2 != NULL) {
++#ifdef DEBUG
++ printf("Setting %s to \"%s\" in PAM environment.\n",
++ KRB5_ENV_CCNAME, ccname);
++#endif
++ sprintf(ccname2, "%s=%s", KRB5_ENV_CCNAME, ccname);
++ pam_putenv(appl_pamh, ccname2);
++ }
++ }
++}
++static int
++appl_pam_start(const char *service, int interactive,
++ const char *login_username,
++ const char *non_interactive_password,
++ const char *tty)
++{
++ static int exit_handler_registered;
++ static struct appl_pam_non_interactive_args args;
++ int ret = 0;
++ if (appl_pam_started &&
++ (strcmp(login_username, appl_pam_user) != 0)) {
++ appl_pam_cleanup();
++ appl_pam_user = NULL;
++ }
++ if (!appl_pam_started) {
++#ifdef DEBUG
++ printf("Starting PAM up (service=\"%s\",user=\"%s\").\n",
++ service, login_username);
++#endif
++ memset(&appl_pam_conv, 0, sizeof(appl_pam_conv));
++ appl_pam_conv.conv = interactive ?
++ &appl_pam_interactive_converse :
++ &appl_pam_non_interactive_converse;
++ memset(&args, 0, sizeof(args));
++ args.user = strdup(login_username);
++ args.password = strdup(non_interactive_password);
++ appl_pam_conv.appdata_ptr = &args;
++ ret = pam_start(service, login_username,
++ &appl_pam_conv, &appl_pamh);
++ if (ret == 0) {
++ if (tty != NULL) {
++#ifdef DEBUG
++ printf("Setting PAM_TTY to \"%s\".\n", tty);
++#endif
++ pam_set_item(appl_pamh, PAM_TTY, tty);
++ }
++ if (!exit_handler_registered &&
++ (atexit(appl_pam_cleanup) != 0)) {
++ pam_end(appl_pamh, 0);
++ appl_pamh = NULL;
++ ret = -1;
++ } else {
++ appl_pam_started = 1;
++ appl_pam_starter = getpid();
++ appl_pam_user = strdup(login_username);
++ exit_handler_registered = 1;
++ }
++ }
++ }
++ return ret;
++}
++int
++appl_pam_authenticate(const char *service, int interactive,
++ const char *login_username,
++ const char *non_interactive_password,
++ const char *tty)
++{
++ int ret;
++ ret = appl_pam_start(service, interactive, login_username,
++ non_interactive_password, tty);
++ if (ret == 0) {
++ ret = pam_authenticate(appl_pamh, 0);
++ }
++ return ret;
++}
++int
++appl_pam_acct_mgmt(const char *service, int interactive,
++ const char *login_username,
++ const char *non_interactive_password,
++ const char *tty)
++{
++ int ret;
++ appl_pam_pwchange_required = 0;
++ ret = appl_pam_start(service, interactive, login_username,
++ non_interactive_password, tty);
++ if (ret == 0) {
++#ifdef DEBUG
++ printf("Calling pam_acct_mgmt().\n");
++#endif
++ ret = pam_acct_mgmt(appl_pamh, 0);
++ switch (ret) {
++ case PAM_IGNORE:
++ ret = 0;
++ break;
++ case PAM_NEW_AUTHTOK_REQD:
++ appl_pam_pwchange_required = 1;
++ ret = 0;
++ break;
++ default:
++ break;
++ }
++ }
++ return ret;
++}
++int
++appl_pam_requires_chauthtok(void)
++{
++ return appl_pam_pwchange_required;
++}
++int
++appl_pam_chauthtok(void)
++{
++ int ret = 0;
++ if (appl_pam_started) {
++#ifdef DEBUG
++ printf("Changing PAM authentication token.\n");
++#endif
++ ret = pam_chauthtok(appl_pamh, 0);
++ }
++ return ret;
++}
++int
++appl_pam_session_open(void)
++{
++ int ret = 0;
++ if (appl_pam_started) {
++#ifdef DEBUG
++ printf("Opening PAM session.\n");
++#endif
++ ret = pam_open_session(appl_pamh, 0);
++ if (ret == 0) {
++ appl_pam_session_opened = 1;
++ }
++ }
++ return ret;
++}
++int
++appl_pam_setenv(void)
++{
++ int ret = 0;
++#ifdef HAVE_PAM_GETENVLIST
++#ifdef HAVE_PUTENV
++ int i;
++ char **list;
++ if (appl_pam_started) {
++ list = pam_getenvlist(appl_pamh);
++ for (i = 0; ((list != NULL) && (list[i] != NULL)); i++) {
++#ifdef DEBUG
++ printf("Setting \"%s\" in environment.\n", list[i]);
++#endif
++ putenv(list[i]);
++ }
++ }
++#endif
++#endif
++ return ret;
++}
++int
++appl_pam_cred_init(void)
++{
++ int ret = 0;
++ if (appl_pam_started) {
++#ifdef DEBUG
++ printf("Initializing PAM credentials.\n");
++#endif
++ ret = pam_setcred(appl_pamh, PAM_ESTABLISH_CRED);
++ if (ret == 0) {
++ appl_pam_creds_initialized = 1;
++ }
++ }
++ return ret;
++}
++#endif
+--- /dev/null 2007-06-22 10:29:46.741860805 -0400
++++ krb5-1.6.1/src/appl/bsd/pam.h 2007-06-22 14:27:05.000000000 -0400
+@@ -0,0 +1,61 @@
++/*
++ * src/appl/bsd/pam.h
++ *
++ * Copyright 2007 Red Hat, Inc.
++ *
++ * All Rights Reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * Redistributions of source code must retain the above copyright notice, this
++ * list of conditions and the following disclaimer.
++ *
++ * Redistributions in binary form must reproduce the above copyright notice,
++ * this list of conditions and the following disclaimer in the documentation
++ * and/or other materials provided with the distribution.
++ *
++ * Neither the name of Red Hat, Inc. nor the names of its contributors may be
++ * used to endorse or promote products derived from this software without
++ * specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
++ * POSSIBILITY OF SUCH DAMAGE.
++ *
++ * Convenience wrappers for using PAM.
++ */
++
++#include <krb5.h>
++#ifdef HAVE_SECURITY_PAM_APPL_H
++#include <security/pam_appl.h>
++#endif
++
++#define USE_PAM_CONFIGURATION_KEYWORD "use_pam"
++
++#ifdef USE_PAM
++int appl_pam_enabled(krb5_context context, const char *section);
++int appl_pam_authenticate(const char *service, int interactive,
++ const char *local_username,
++ const char *non_interactive_password,
++ const char *tty);
++int appl_pam_acct_mgmt(const char *service, int interactive,
++ const char *local_username,
++ const char *non_interactive_password,
++ const char *tty);
++int appl_pam_requires_chauthtok(void);
++int appl_pam_chauthtok(void);
++void appl_pam_set_forwarded_ccname(const char *ccname);
++int appl_pam_session_open(void);
++int appl_pam_setenv(void);
++int appl_pam_cred_init(void);
++void appl_pam_cleanup(void);
++#endif
+--- krb5-1.6.1/src/appl/gssftp/ftpd/Makefile.in 2006-12-01 19:10:25.000000000 -0500
++++ krb5-1.6.1/src/appl/gssftp/ftpd/Makefile.in 2007-06-21 17:39:57.000000000 -0400
+@@ -14,23 +14,25 @@ SETENVOBJ=@SETENVOBJ@
+ LIBOBJS=@LIBOBJS@
+ COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a
+ FTPD_LIBS=@FTPD_LIBS@
++PAM_LIBS=@PAM_LIBS@
+
+ SRCS = $(srcdir)/ftpd.c ftpcmd.c $(srcdir)/popen.c \
+ $(srcdir)/vers.c \
+ $(srcdir)/../ftp/glob.c \
+ $(srcdir)/../ftp/radix.c \
+ $(srcdir)/../ftp/secure.c \
++ $(srcdir)/../../bsd/pam.c \
+ $(srcdir)/../../bsd/getdtablesize.c $(SETENVSRC)
+
+ OBJS = ftpd.o ftpcmd.o glob.o popen.o vers.o radix.o \
+- secure.o $(LIBOBJS) $(SETENVOBJ)
++ secure.o pam.o getdtablesize.o $(LIBOBJS) $(SETENVOBJ)
+
+ LOCALINCLUDES = -I$(srcdir)/.. -I$(srcdir) @KRB4_INCLUDES@
+
+ all:: ftpd
+
+ ftpd: $(OBJS) $(PTY_DEPLIB) $(GSS_DEPLIBS) $(KRB4COMPAT_DEPLIBS)
+- $(CC_LINK) -o $@ $(OBJS) $(FTPD_LIBS) $(PTY_LIB) $(UTIL_LIB) $(GSS_LIBS) $(KRB4COMPAT_LIBS)
++ $(CC_LINK) -o $@ $(OBJS) $(FTPD_LIBS) $(PTY_LIB) $(UTIL_LIB) $(GSS_LIBS) $(PAM_LIBS) $(KRB4COMPAT_LIBS)
+
+ generate-files-mac: ftpcmd.c
+
+@@ -62,6 +64,8 @@ secure.o: $(srcdir)/../ftp/secure.c
+
+ getdtablesize.o: $(srcdir)/../../bsd/getdtablesize.c
+ $(CC) -c $(ALL_CFLAGS) $(srcdir)/../../bsd/getdtablesize.c
++pam.o: $(srcdir)/../../bsd/pam.c
++ $(CC) -c $(ALL_CFLAGS) $(srcdir)/../../bsd/pam.c
+
+ setenv.o: $(srcdir)/../../bsd/setenv.c
+ $(CC) -c $(ALL_CFLAGS) $(srcdir)/../../bsd/setenv.c
+--- krb5-1.6.1/src/appl/gssftp/ftpd/ftpd.c 2006-08-08 15:26:40.000000000 -0400
++++ krb5-1.6.1/src/appl/gssftp/ftpd/ftpd.c 2007-06-22 14:28:09.000000000 -0400
+@@ -70,6 +70,9 @@ static char sccsid[] = "@(#)ftpd.c 5.40
+ #ifdef HAVE_SHADOW
+ #include <shadow.h>
+ #endif
++#ifdef USE_PAM
++#include "../../bsd/pam.h"
++#endif
+ #include <grp.h>
+ #include <setjmp.h>
+ #ifndef POSIX_SETJMP
+@@ -903,6 +906,10 @@ end_login()
+ (void) krb5_seteuid((uid_t)0);
+ if (logged_in)
+ pty_logwtmp(ttyline, "", "");
++#ifdef USE_PAM
++ if (appl_pam_enabled(kcontext, "ftpd"))
++ appl_pam_cleanup();
++#endif
+ if (have_creds) {
+ #ifdef GSSAPI
+ krb5_cc_destroy(kcontext, ccache);
+@@ -1073,9 +1080,17 @@ pass(passwd)
+ * kpass fails and the user has no local password
+ * kpass fails and the provided password doesn't match pw
+ */
+- if (pw == NULL || (!kpass(pw->pw_name, passwd) &&
+- (want_creds || !*pw->pw_passwd ||
+- strcmp(xpasswd, pw->pw_passwd)))) {
++ if ((pw == NULL) ||
++#ifdef USE_PAM
++ appl_pam_enabled(kcontext, "ftpd") ?
++ (appl_pam_authenticate(FTP_PAM_SERVICE, 0,
++ pw->pw_name, passwd,
++ FTP_PAM_SERVICE) != 0) :
++#endif
++ (!kpass(pw->pw_name, passwd) &&
++ (want_creds ||
++ !*pw->pw_passwd ||
++ strcmp(xpasswd, pw->pw_passwd)))) {
+ pw = NULL;
+ sleep(5);
+ if (++login_attempts >= 3) {
+@@ -1092,6 +1107,17 @@ pass(passwd)
+ }
+ login_attempts = 0; /* this time successful */
+
++#ifdef USE_PAM
++ if (appl_pam_enabled(kcontext, "ftpd")) {
++ if (appl_pam_acct_mgmt(FTP_PAM_SERVICE, 0,
++ pw->pw_name, passwd,
++ FTP_PAM_SERVICE) != 0) {
++ reply(530, "Login incorrect.");
++ return;
++ }
++ }
++#endif
++
+ login(passwd, 0);
+ return;
+ }
+@@ -1110,6 +1136,18 @@ login(passwd, logincode)
+ chown(tkt_string(), pw->pw_uid, pw->pw_gid);
+ #endif
+ }
++#ifdef USE_PAM
++ if (appl_pam_enabled(kcontext, "ftpd")) {
++ if (appl_pam_session_open() != 0) {
++ reply(550, "Can't open PAM session.");
++ goto bad;
++ }
++ if (appl_pam_cred_init() != 0) {
++ reply(550, "Can't establish PAM credentials.");
++ goto bad;
++ }
++ }
++#endif
+
+ (void) krb5_setegid((gid_t)pw->pw_gid);
+ (void) initgroups(pw->pw_name, pw->pw_gid);
+@@ -2125,6 +2163,10 @@ dologout(status)
+ dest_tkt();
+ #endif
+ }
++#ifdef USE_PAM
++ if (appl_pam_enabled(kcontext, "ftpd"))
++ appl_pam_cleanup();
++#endif
+ /* beware of flushing buffers after a SIGPIPE */
+ _exit(status);
+ }
+--- krb5-1.6.1/src/appl/gssftp/configure.in 2006-03-31 16:00:40.000000000 -0500
++++ krb5-1.6.1/src/appl/gssftp/configure.in 2007-06-21 17:39:57.000000000 -0400
+@@ -17,6 +17,7 @@ DECLARE_SYS_ERRLIST
+ AC_REPLACE_FUNCS(getdtablesize)
+ AC_CHECK_FUNCS(getcwd getdtablesize getusershell seteuid setreuid setresuid strerror getenv)
+ AC_CHECK_LIB(crypt,crypt) dnl
++KRB5_WITH_PAM
+ KRB5_AC_LIBUTIL
+ dnl
+ dnl copied from appl/bsd/configure.in
+--- krb5-1.6.1/src/configure.in 2007-06-21 17:39:57.000000000 -0400
++++ krb5-1.6.1/src/configure.in 2007-06-21 17:39:57.000000000 -0400
+@@ -929,6 +929,8 @@ if false; then
+ AC_CONFIG_SUBDIRS(plugins/locate/python)
+ fi
+
++KRB5_WITH_PAM
++
+ AC_CONFIG_FILES(krb5-config, [chmod +x krb5-config])
+
+ mansysconfdir=$sysconfdir
+--- krb5-1.6.1/src/config/pre.in 2007-06-21 17:39:57.000000000 -0400
++++ krb5-1.6.1/src/config/pre.in 2007-06-21 17:39:57.000000000 -0400
+@@ -180,6 +180,7 @@ SRVLIBS = @SRVLIBS@
+ SRVDEPLIBS = @SRVDEPLIBS@
+ CLNTLIBS = @CLNTLIBS@
+ CLNTDEPLIBS = @CLNTDEPLIBS@
++PAM_LIBS = @PAM_LIBS@
+
+ INSTALL=@INSTALL@
+ INSTALL_STRIP=
+--- krb5-1.6.1/src/aclocal.m4 2007-06-21 17:39:57.000000000 -0400
++++ krb5-1.6.1/src/aclocal.m4 2007-06-21 17:39:57.000000000 -0400
+@@ -1823,3 +1823,82 @@ AC_DEFUN(KRB5_AC_KEYRING_CCACHE,[
+ ]))
+ ])dnl
+ dnl
++dnl
++dnl Use PAM instead of local crypt() compare for checking local passwords,
++dnl and perform PAM account, session management, and password-changing where
++dnl appropriate.
++dnl
++AC_DEFUN(KRB5_WITH_PAM,[
++AC_ARG_WITH(pam,[AC_HELP_STRING(--with-pam,[compile with PAM support])],
++ withpam="$withval",withpam=auto)
++AC_ARG_WITH(pam-login-service,[AC_HELP_STRING(--with-login-service,[PAM service name for login ["login"]])],
++ withloginpamservice="$withval",withloginpamservice=login)
++AC_ARG_WITH(pam-kshell-service,[AC_HELP_STRING(--with-kshell-service,[PAM service name for unencrypted rsh ["kshell"]])],
++ withkshellpamservice="$withval",withkshellpamservice=kshell)
++AC_ARG_WITH(pam-ekshell-service,[AC_HELP_STRING(--with-ekshell-service,[PAM service name for encrypted rsh ["ekshell"]])],
++ withekshellpamservice="$withval",withekshellpamservice=ekshell)
++AC_ARG_WITH(pam-ftp-service,[AC_HELP_STRING(--with-ftp-service,[PAM service name for ftpd ["gssftp"]])],
++ withftppamservice="$withval",withftppamservice=gssftp)
++old_LIBS="$LIBS"
++if test "$withpam" != no ; then
++ AC_MSG_RESULT([checking for PAM...])
++ PAM_LIBS=
++
++ AC_CHECK_HEADERS(security/pam_appl.h)
++ if test "x$ac_cv_header_security_pam_appl_h" != xyes ; then
++ if test "$withpam" = auto ; then
++ AC_MSG_RESULT([Unable to locate security/pam_appl.h.])
++ withpam=no
++ else
++ AC_MSG_ERROR([Unable to locate security/pam_appl.h.])
++ fi
++ fi
++
++ LIBS=
++ unset ac_cv_func_pam_start
++ AC_CHECK_FUNCS(putenv pam_start)
++ if test "x$ac_cv_func_pam_start" = xno ; then
++ unset ac_cv_func_pam_start
++ AC_CHECK_LIB(dl,dlopen)
++ AC_CHECK_FUNCS(pam_start)
++ if test "x$ac_cv_func_pam_start" = xno ; then
++ AC_CHECK_LIB(pam,pam_start)
++ unset ac_cv_func_pam_start
++ unset ac_cv_func_pam_getenvlist
++ AC_CHECK_FUNCS(pam_start pam_getenvlist)
++ if test "x$ac_cv_func_pam_start" = xyes ; then
++ PAM_LIBS="$LIBS"
++ else
++ if test "$withpam" = auto ; then
++ AC_MSG_RESULT([Unable to locate libpam.])
++ withpam=no
++ else
++ AC_MSG_ERROR([Unable to locate libpam.])
++ fi
++ fi
++ fi
++ fi
++ if test "$withpam" != no ; then
++ AC_MSG_RESULT([Using PAM.])
++ AC_DEFINE(USE_PAM,1,[Define if Kerberos-aware tools should support PAM])
++ AC_DEFINE_UNQUOTED(LOGIN_PAM_SERVICE,"$withloginpamservice",
++ [Define to the name of the PAM service name to be used by login.])
++ AC_DEFINE_UNQUOTED(KSHELL_PAM_SERVICE,"$withkshellpamservice",
++ [Define to the name of the PAM service name to be used by rshd for unencrypted sessions.])
++ AC_DEFINE_UNQUOTED(EKSHELL_PAM_SERVICE,"$withekshellpamservice",
++ [Define to the name of the PAM service name to be used by rshd for encrypted sessions.])
++ AC_DEFINE_UNQUOTED(FTP_PAM_SERVICE,"$withftppamservice",
++ [Define to the name of the PAM service name to be used by ftpd.])
++ PAM_LIBS="$LIBS"
++ NON_PAM_MAN=".\\\" "
++ PAM_MAN=
++ else
++ PAM_MAN=".\\\" "
++ NON_PAM_MAN=
++ fi
++fi
++LIBS="$old_LIBS"
++AC_SUBST(PAM_LIBS)
++AC_SUBST(PAM_MAN)
++AC_SUBST(NON_PAM_MAN)
++])dnl