[krbdev.mit.edu #5939] PAM support for rshd, login, and ftpd

The RT System itself via RT rt-comment at krbdev.mit.edu
Tue Apr 15 13:57:48 EDT 2008


>From krb5-bugs-incoming-bounces at PCH.MIT.EDU  Tue Apr 15 13:57:42 2008
Received: from pch.mit.edu (PCH.MIT.EDU [18.7.21.90]) by krbdev.mit.edu (8.12.9) with ESMTP
	id m3FHvgHW020493; Tue, 15 Apr 2008 13:57:42 -0400 (EDT)
Received: from pch.mit.edu (pch.mit.edu [127.0.0.1])
	by pch.mit.edu (8.13.6/8.12.8) with ESMTP id m3FHvb1Q021875;
	Tue, 15 Apr 2008 13:57:37 -0400
Received: from pacific-carrier-annex.mit.edu (PACIFIC-CARRIER-ANNEX.MIT.EDU
	[18.7.21.83])
	by pch.mit.edu (8.13.6/8.12.8) with ESMTP id m3FF2Vt4006051
	for <krb5-bugs-incoming at PCH.mit.edu>; Tue, 15 Apr 2008 11:02:31 -0400
Received: from mit.edu (M24-004-BARRACUDA-2.MIT.EDU [18.7.7.112])
	by pacific-carrier-annex.mit.edu (8.13.6/8.9.2) with ESMTP id
	m3FF2I1k008282
	for <krb5-bugs at mit.edu>; Tue, 15 Apr 2008 11:02:19 -0400 (EDT)
Received: from mx1.redhat.com (mx1.redhat.com [66.187.233.31])
	by mit.edu (Spam Firewall) with ESMTP id 4E75A108F2D8
	for <krb5-bugs at mit.edu>; Tue, 15 Apr 2008 11:01:53 -0400 (EDT)
Received: from int-mx1.corp.redhat.com (int-mx1.corp.redhat.com
	[172.16.52.254])
	by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id m3FF1k9X018346
	for <krb5-bugs at mit.edu>; Tue, 15 Apr 2008 11:01:46 -0400
Received: from blade.boston.redhat.com (blade.boston.redhat.com [172.16.80.50])
	by int-mx1.corp.redhat.com (8.13.1/8.13.1) with ESMTP id m3FF1jVo000802
	for <krb5-bugs at mit.edu>; Tue, 15 Apr 2008 11:01:45 -0400
Received: from blade.boston.redhat.com (localhost.localdomain [127.0.0.1])
	by blade.boston.redhat.com (8.14.2/8.14.2) with ESMTP id m3FF1jsU002642
	for <krb5-bugs at mit.edu>; Tue, 15 Apr 2008 11:01:45 -0400
Received: (from nalin at localhost)
	by blade.boston.redhat.com (8.14.2/8.14.2/Submit) id m3FF1jSs002641;
	Tue, 15 Apr 2008 11:01:45 -0400
Date: Tue, 15 Apr 2008 11:01:45 -0400
Message-Id: <200804151501.m3FF1jSs002641 at blade.boston.redhat.com>
To: krb5-bugs at mit.edu
From: nalin at redhat.com
X-send-pr-version: 3.99
X-Scanned-By: MIMEDefang 2.42
X-Scanned-By: MIMEDefang 2.58 on 172.16.52.254
X-Spam-Score: 2.779
X-Spam-Level: ** (2.779)
X-Spam-Flag: NO
X-Mailman-Approved-At: Tue, 15 Apr 2008 13:57:35 -0400
X-BeenThere: krb5-bugs-incoming at mailman.mit.edu
X-Mailman-Version: 2.1.6
Precedence: list
Reply-To: nalin at redhat.com
Sender: krb5-bugs-incoming-bounces at PCH.MIT.EDU
Errors-To: krb5-bugs-incoming-bounces at PCH.MIT.EDU


>Submitter-Id:	net
>Originator:	nalin at redhat.com
>Organization:
>Confidential:	no
>Synopsis:	PAM support for rshd, login, and ftpd
>Severity:	non-critical
>Priority:	low
>Category:	krb5-appl
>Class:		change-request
>Release:	1.6.3
>Environment:
	
System: Linux blade.boston.redhat.com 2.6.23-6.fc8 #1 SMP Thu Oct 11 13:36:39 EDT 2007 x86_64 x86_64 x86_64 GNU/Linux
Architecture: x86_64

>Description:
	Users who log in using the various Kerberos-aware applications
	(rshd, rlogin and telnet (login.krb5)) don't have PAM account
	and session management performed for them.  On systems where
	process limits are set by PAM, their sessions are exempt from
	these limits.  On systems where session management is used to
	hook in AFS token setting, users who FTP in don't have access to
	their home directories.
>How-To-Repeat:
	Log in via rlogin or telnet, execute the "limit" or "ulimit -a"
	command via rsh, or access the system using ftpd.
>Fix:
	I'm including a patch which makes changes to krshd, login, and
	ftpd.  I'm not really sure about what the right thing to do for
	"ksu" is, so for now at least, this patch doesn't touch it.

	It modifies 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
	(this required moving the "chdir" call).  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.

	It modifies ftpd so that authentication with a plaintext password
	goes through PAM, and it performs PAM account and session
	management for all clients.  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, ftpd'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.

	It modifies 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 for all users (prompting for a
	password change if need be), and that it performs session
	management for all users.  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.  The default at build-time is to enable PAM support if
	it's found to be available, but it can be forced on or off with
	the "--with-pam" or "--without-pam" option.

	The individual PAM service names can be set with these arguments:
	login.krb5: --with-pam-login-service (default is "login")
	rshd without encryption: --with-pam-kshell-service (default is "kshell")
	rshd with encryption: --with-pam-ekshell-service (default is "ekshell")
	gssftpd: --with-pam-ftp-service (default is "gssftp)

Index: src/configure.in
===================================================================
--- src/configure.in	(revision 20303)
+++ src/configure.in	(working copy)
@@ -1073,6 +1073,8 @@
 dnl
 AC_CONFIG_SUBDIRS(appl/libpty appl/bsd appl/gssftp appl/telnet)
 
+KRB5_WITH_PAM
+
 AC_CONFIG_FILES(krb5-config, [chmod +x krb5-config])
 V5_AC_OUTPUT_MAKEFILE(.
 
Index: src/appl/gssftp/configure.in
===================================================================
--- src/appl/gssftp/configure.in	(revision 20303)
+++ src/appl/gssftp/configure.in	(working copy)
@@ -17,6 +17,7 @@
 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
Index: src/appl/gssftp/ftpd/Makefile.in
===================================================================
--- src/appl/gssftp/ftpd/Makefile.in	(revision 20303)
+++ src/appl/gssftp/ftpd/Makefile.in	(working copy)
@@ -14,23 +14,25 @@
 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 @@
 
 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
Index: src/appl/gssftp/ftpd/ftpd.c
===================================================================
--- src/appl/gssftp/ftpd/ftpd.c	(revision 20303)
+++ src/appl/gssftp/ftpd/ftpd.c	(working copy)
@@ -70,6 +70,9 @@
 #ifdef HAVE_SHADOW
 #include <shadow.h>
 #endif
+#ifdef USE_PAM
+#include "../../bsd/pam.h"
+#endif
 #include <grp.h> 
 #include <setjmp.h>
 #ifndef POSIX_SETJMP
@@ -803,6 +806,17 @@
 		}
 #endif /* KRB5_KRB4_COMPAT */
 
+#ifdef USE_PAM
+		if (appl_pam_enabled(kcontext, "ftpd")) {
+			if (appl_pam_acct_mgmt(FTP_PAM_SERVICE,
+					       appl_pam_noninteractive,
+					       pw->pw_name, "",
+					       FTP_PAM_SERVICE) != 0) {
+				reply(530, "Login incorrect.");
+				return;
+			}
+		}
+#endif
 		if (!authorized && authlevel == AUTHLEVEL_AUTHORIZE) {
 			strncat(buf, "; Access denied.",
 				sizeof(buf) - strlen(buf) - 1);
@@ -903,6 +917,10 @@
 	(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 +1091,18 @@
 		 *   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,
+		    			   appl_pam_noninteractive,
+					   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 +1119,17 @@
 	}
 	login_attempts = 0;		/* this time successful */
 
+#ifdef USE_PAM
+	if (appl_pam_enabled(kcontext, "ftpd")) {
+		if (appl_pam_acct_mgmt(FTP_PAM_SERVICE, appl_pam_noninteractive,
+				       pw->pw_name, passwd,
+				       FTP_PAM_SERVICE) != 0) {
+			reply(530, "Login incorrect.");
+			return;
+		}
+	}
+#endif
+
 	login(passwd, 0);
 	return;
 }
@@ -1110,6 +1148,18 @@
 		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);
@@ -2114,6 +2164,10 @@
 		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);
 }
Index: src/appl/bsd/Makefile.in
===================================================================
--- src/appl/bsd/Makefile.in	(revision 20303)
+++ src/appl/bsd/Makefile.in	(working copy)
@@ -14,13 +14,14 @@
 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 @@
 		${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 @@
 # 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
Index: src/appl/bsd/configure.in
===================================================================
--- src/appl/bsd/configure.in	(revision 20303)
+++ src/appl/bsd/configure.in	(working copy)
@@ -24,6 +24,7 @@
     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.
Index: src/appl/bsd/login.c
===================================================================
--- src/appl/bsd/login.c	(revision 20303)
+++ src/appl/bsd/login.c	(working copy)
@@ -159,6 +159,11 @@
 #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 @@
     char *flagname;
     int *flag;
 } login_conf_set[] = {
+#if defined(USE_PAM) && defined(USE_PAM_CONFIGURATION_KEYWORD)
+    {USE_PAM_CONFIGURATION_KEYWORD, &login_use_pam},
+#endif
 #ifdef KRB5_GET_TICKETS
     {"krb5_get_tickets", &login_krb5_get_tickets},
 #endif
@@ -1292,6 +1300,19 @@
 	if (!unix_needs_passwd())
 	    break;
 
+#ifdef USE_PAM
+	if (login_use_pam) {
+	    if (appl_pam_authenticate(LOGIN_PAM_SERVICE, appl_pam_interactive,
+				      username, "",
+				      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 +1427,25 @@
     /* 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, appl_pam_interactive,
+			       username, "",
+			       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,7 +1486,22 @@
 	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);
 	if (chdir("/"))
@@ -1792,6 +1847,11 @@
     }
 #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)
Index: src/appl/bsd/krshd.c
===================================================================
--- src/appl/bsd/krshd.c	(revision 20303)
+++ src/appl/bsd/krshd.c	(working copy)
@@ -181,6 +181,10 @@
 #include <arpa/nameser.h>
 #endif
 
+#ifdef USE_PAM
+#include "pam.h"
+#endif
+
 #ifndef MAXDNAME
 #define MAXDNAME 256 /*per the rfc*/
 #endif
@@ -201,6 +205,7 @@
 
 int require_encrypt = 0;
 int do_encrypt = 0;
+int force_fork = 0;
 int anyport = 0;
 char *kprogdir = KPROGDIR;
 int netf;
@@ -1081,14 +1086,6 @@
     }
 #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)
@@ -1147,11 +1144,49 @@
 	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,
+			       appl_pam_noninteractive,
+			       locuser,
+			       "",
+			       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)) {
@@ -1191,7 +1226,7 @@
     
     (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;
@@ -1503,6 +1538,15 @@
 
     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
Index: src/appl/bsd/pam.c
===================================================================
--- src/appl/bsd/pam.c	(revision 0)
+++ src/appl/bsd/pam.c	(revision 0)
@@ -0,0 +1,443 @@
+/*
+ * src/appl/bsd/pam.c
+ *
+ * Copyright 2007,2008 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 256
+#endif
+
+static int appl_pam_started;
+static pid_t appl_pam_starter = -1;
+static int appl_pam_session_is_open;
+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;
+#ifdef USE_PAM_CONFIGURATION_KEYWORD
+	if ((context != NULL) && (context->profile != NULL)) {
+		if (profile_get_boolean(context->profile,
+					section,
+					USE_PAM_CONFIGURATION_KEYWORD,
+					NULL,
+					enabled, &enabled) != 0) {
+			enabled = 1;
+		}
+	}
+#endif
+	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_is_open) {
+#ifdef DEBUG
+		printf("Closing PAM session.\n");
+#endif
+		pam_close_session(appl_pamh, 0);
+		appl_pam_session_is_open = 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++) {
+#if defined(__LINUX_PAM__) || defined(_OPENPAM)
+		/* Linux-PAM and OpenPAM treat messages as an array of
+		 * pointers. */
+		message = msg[i];
+#else
+		/* Other PAM implementations treat messages as a pointer to an
+		 * array. */
+		message = &((*msg)[i]);
+#endif
+		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[strcspn(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++) {
+#if defined(__LINUX_PAM__) || defined(_OPENPAM)
+		/* Linux-PAM and OpenPAM treat messages as an array of
+		 * pointers. */
+		message = msg[i];
+#else
+		/* Other PAM implementations treat messages as a pointer to an
+		 * array. */
+		message = &((*msg)[i]);
+#endif
+		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, enum appl_pam_interactive_flag interactive,
+	       const char *login_username,
+	       const char *password_if_not_interactive,
+	       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) ?
+				     &appl_pam_interactive_converse :
+				     &appl_pam_non_interactive_converse;
+		memset(&args, 0, sizeof(args));
+		args.user = strdup(login_username);
+		args.password = password_if_not_interactive ?
+				strdup(password_if_not_interactive) :
+				NULL;
+		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,
+		      enum appl_pam_interactive_flag interactive,
+		      const char *login_username,
+		      const char *password_if_not_interactive,
+		      const char *tty)
+{
+	int ret;
+	ret = appl_pam_start(service, interactive, login_username,
+			     password_if_not_interactive, tty);
+	if (ret == 0) {
+		ret = pam_authenticate(appl_pamh, 0);
+	}
+	return ret;
+}
+
+int
+appl_pam_acct_mgmt(const char *service,
+		   enum appl_pam_interactive_flag interactive,
+		   const char *login_username,
+		   const char *password_if_not_interactive,
+		   const char *tty)
+{
+	int ret;
+	appl_pam_pwchange_required = 0;
+	ret = appl_pam_start(service, interactive, login_username,
+			     password_if_not_interactive, 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 expired authentication token.\n");
+#endif
+		ret = pam_chauthtok(appl_pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
+	}
+	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_is_open = 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
Index: src/appl/bsd/pam.h
===================================================================
--- src/appl/bsd/pam.h	(revision 0)
+++ src/appl/bsd/pam.h	(revision 0)
@@ -0,0 +1,65 @@
+/*
+ * 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
+enum appl_pam_interactive_flag {
+	appl_pam_interactive,
+	appl_pam_noninteractive
+};
+int appl_pam_enabled(krb5_context context, const char *section);
+int appl_pam_authenticate(const char *service, enum appl_pam_interactive_flag,
+			  const char *local_username,
+			  const char *password_if_not_interactive,
+			  const char *tty);
+int appl_pam_acct_mgmt(const char *service, enum appl_pam_interactive_flag,
+		       const char *local_username,
+		       const char *password_if_not_interactive,
+		       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
Index: src/config/pre.in
===================================================================
--- src/config/pre.in	(revision 20303)
+++ src/config/pre.in	(working copy)
@@ -185,6 +185,7 @@
 SRVDEPLIBS = @SRVDEPLIBS@
 CLNTLIBS = @CLNTLIBS@
 CLNTDEPLIBS = @CLNTDEPLIBS@
+PAM_LIBS = @PAM_LIBS@
 
 INSTALL=@INSTALL@
 INSTALL_STRIP=
Index: src/aclocal.m4
===================================================================
--- src/aclocal.m4	(revision 20303)
+++ src/aclocal.m4	(working copy)
@@ -1785,3 +1785,75 @@
       ]))
 ])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, building without PAM support.])
+			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, building without PAM support])
+					withpam=no
+				else
+					AC_MSG_ERROR([Unable to locate libpam.])
+				fi
+			fi
+		fi
+	fi
+	if test "$withpam" != no ; then
+		AC_MSG_RESULT([Building with PAM support])
+		AC_DEFINE(USE_PAM,1,[Define if Kerberos-aware applications should support PAM.])
+		AC_DEFINE_UNQUOTED(LOGIN_PAM_SERVICE,"$withloginpamservice",
+				   [Define to the name of the PAM service name to be used by login.krb5.])
+		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"
+	fi
+fi
+LIBS="$old_LIBS"
+AC_SUBST(PAM_LIBS)
+])dnl




More information about the krb5-bugs mailing list