Blame SOURCES/downstream-ksu-pam-integration.patch

10fa70
From 664bdd73b620f00d42e36e3888805fe0f035c8ee Mon Sep 17 00:00:00 2001
afd354
From: Robbie Harwood <rharwood@redhat.com>
afd354
Date: Tue, 23 Aug 2016 16:29:58 -0400
10fa70
Subject: [PATCH] [downstream] ksu pam integration
afd354
afd354
Modify ksu so that it performs account and session management on behalf of
afd354
the target user account, mimicking the action of regular su.  The default
afd354
service name is "ksu", because on Fedora at least the configuration used
afd354
is determined by whether or not a login shell is being opened, and so
afd354
this may need to vary, too.  At run-time, ksu's behavior can be reset to
afd354
the earlier, non-PAM behavior by setting "use_pam" to false in the [ksu]
afd354
section of /etc/krb5.conf.
afd354
afd354
When enabled, ksu gains a dependency on libpam.
afd354
afd354
Originally RT#5939, though it's changed since then to perform the account
afd354
and session management before dropping privileges, and to apply on top of
afd354
changes we're proposing for how it handles cache collections.
10fa70
10fa70
Last-updated: krb5-1.18-beta1
10fa70
(cherry picked from commit a7322a84657752c886c317a6994a9fc7a4a70ca5)
afd354
---
10fa70
 src/aclocal.m4              |  69 +++++++
afd354
 src/clients/ksu/Makefile.in |   8 +-
afd354
 src/clients/ksu/main.c      |  88 +++++++-
afd354
 src/clients/ksu/pam.c       | 389 ++++++++++++++++++++++++++++++++++++
afd354
 src/clients/ksu/pam.h       |  57 ++++++
10fa70
 src/configure.ac            |   2 +
10fa70
 6 files changed, 610 insertions(+), 3 deletions(-)
afd354
 create mode 100644 src/clients/ksu/pam.c
afd354
 create mode 100644 src/clients/ksu/pam.h
afd354
afd354
diff --git a/src/aclocal.m4 b/src/aclocal.m4
10fa70
index 2394f7e33..830203683 100644
afd354
--- a/src/aclocal.m4
afd354
+++ b/src/aclocal.m4
10fa70
@@ -1675,3 +1675,72 @@ if test "$with_ldap" = yes; then
10fa70
   OPENLDAP_PLUGIN=yes
10fa70
 fi
afd354
 ])dnl
10fa70
+dnl
afd354
+dnl
afd354
+dnl Use PAM instead of local crypt() compare for checking local passwords,
afd354
+dnl and perform PAM account, session management, and password-changing where
afd354
+dnl appropriate.
afd354
+dnl 
afd354
+AC_DEFUN(KRB5_WITH_PAM,[
afd354
+AC_ARG_WITH(pam,[AC_HELP_STRING(--with-pam,[compile with PAM support])],
afd354
+	    withpam="$withval",withpam=auto)
afd354
+AC_ARG_WITH(pam-ksu-service,[AC_HELP_STRING(--with-ksu-service,[PAM service name for ksu ["ksu"]])],
afd354
+	    withksupamservice="$withval",withksupamservice=ksu)
afd354
+old_LIBS="$LIBS"
afd354
+if test "$withpam" != no ; then
afd354
+	AC_MSG_RESULT([checking for PAM...])
afd354
+	PAM_LIBS=
afd354
+
afd354
+	AC_CHECK_HEADERS(security/pam_appl.h)
afd354
+	if test "x$ac_cv_header_security_pam_appl_h" != xyes ; then
afd354
+		if test "$withpam" = auto ; then
afd354
+			AC_MSG_RESULT([Unable to locate security/pam_appl.h.])
afd354
+			withpam=no
afd354
+		else
afd354
+			AC_MSG_ERROR([Unable to locate security/pam_appl.h.])
afd354
+		fi
afd354
+	fi
afd354
+
afd354
+	LIBS=
afd354
+	unset ac_cv_func_pam_start
afd354
+	AC_CHECK_FUNCS(putenv pam_start)
afd354
+	if test "x$ac_cv_func_pam_start" = xno ; then
afd354
+		unset ac_cv_func_pam_start
afd354
+		AC_CHECK_LIB(dl,dlopen)
afd354
+		AC_CHECK_FUNCS(pam_start)
afd354
+		if test "x$ac_cv_func_pam_start" = xno ; then
afd354
+			AC_CHECK_LIB(pam,pam_start)
afd354
+			unset ac_cv_func_pam_start
afd354
+			unset ac_cv_func_pam_getenvlist
afd354
+			AC_CHECK_FUNCS(pam_start pam_getenvlist)
afd354
+			if test "x$ac_cv_func_pam_start" = xyes ; then
afd354
+				PAM_LIBS="$LIBS"
afd354
+			else
afd354
+				if test "$withpam" = auto ; then
afd354
+					AC_MSG_RESULT([Unable to locate libpam.])
afd354
+					withpam=no
afd354
+				else
afd354
+					AC_MSG_ERROR([Unable to locate libpam.])
afd354
+				fi
afd354
+			fi
afd354
+		fi
afd354
+	fi
afd354
+	if test "$withpam" != no ; then
afd354
+		AC_MSG_NOTICE([building with PAM support])
afd354
+		AC_DEFINE(USE_PAM,1,[Define if Kerberos-aware tools should support PAM])
afd354
+		AC_DEFINE_UNQUOTED(KSU_PAM_SERVICE,"$withksupamservice",
afd354
+				   [Define to the name of the PAM service name to be used by ksu.])
afd354
+		PAM_LIBS="$LIBS"
afd354
+		NON_PAM_MAN=".\\\" "
afd354
+		PAM_MAN=
afd354
+	else
afd354
+		PAM_MAN=".\\\" "
afd354
+		NON_PAM_MAN=
afd354
+	fi
afd354
+fi
afd354
+LIBS="$old_LIBS"
afd354
+AC_SUBST(PAM_LIBS)
afd354
+AC_SUBST(PAM_MAN)
afd354
+AC_SUBST(NON_PAM_MAN)
afd354
+])dnl
10fa70
+
afd354
diff --git a/src/clients/ksu/Makefile.in b/src/clients/ksu/Makefile.in
10fa70
index 8b4edce4d..9d58f29b5 100644
afd354
--- a/src/clients/ksu/Makefile.in
afd354
+++ b/src/clients/ksu/Makefile.in
afd354
@@ -3,12 +3,14 @@ BUILDTOP=$(REL)..$(S)..
10fa70
 DEFINES = -DGET_TGT_VIA_PASSWD -DPRINC_LOOK_AHEAD -DCMD_PATH='"/usr/local/sbin /usr/local/bin /sbin /bin /usr/sbin /usr/bin"'
afd354
 
afd354
 KSU_LIBS=@KSU_LIBS@
afd354
+PAM_LIBS=@PAM_LIBS@
afd354
 
afd354
 SRCS = \
afd354
 	$(srcdir)/krb_auth_su.c \
afd354
 	$(srcdir)/ccache.c \
afd354
 	$(srcdir)/authorization.c \
afd354
 	$(srcdir)/main.c \
afd354
+	$(srcdir)/pam.c \
afd354
 	$(srcdir)/heuristic.c \
afd354
 	$(srcdir)/xmalloc.c \
afd354
 	$(srcdir)/setenv.c
afd354
@@ -17,13 +19,17 @@ OBJS = \
afd354
 	ccache.o \
afd354
 	authorization.o \
afd354
 	main.o \
afd354
+	pam.o \
afd354
 	heuristic.o \
afd354
 	xmalloc.o @SETENVOBJ@
afd354
 
afd354
 all: ksu
afd354
 
afd354
 ksu: $(OBJS) $(KRB5_BASE_DEPLIBS)
afd354
-	$(CC_LINK) -o $@ $(OBJS) $(KRB5_BASE_LIBS) $(KSU_LIBS)
afd354
+	$(CC_LINK) -o $@ $(OBJS) $(KRB5_BASE_LIBS) $(KSU_LIBS) $(PAM_LIBS)
afd354
+
afd354
+pam.o: pam.c
afd354
+	$(CC) $(ALL_CFLAGS) -c $<
afd354
 
afd354
 clean:
afd354
 	$(RM) ksu
afd354
diff --git a/src/clients/ksu/main.c b/src/clients/ksu/main.c
10fa70
index 57c349200..508242e0e 100644
afd354
--- a/src/clients/ksu/main.c
afd354
+++ b/src/clients/ksu/main.c
afd354
@@ -26,6 +26,7 @@
afd354
  * KSU was writen by:  Ari Medvinsky, ari@isi.edu
afd354
  */
afd354
 
afd354
+#include "autoconf.h"
afd354
 #include "ksu.h"
afd354
 #include "adm_proto.h"
afd354
 #include <sys/types.h>
afd354
@@ -33,6 +34,10 @@
afd354
 #include <signal.h>
afd354
 #include <grp.h>
afd354
 
afd354
+#ifdef USE_PAM
afd354
+#include "pam.h"
afd354
+#endif
afd354
+
afd354
 /* globals */
afd354
 char * prog_name;
afd354
 int auth_debug =0;
afd354
@@ -40,6 +45,7 @@ char k5login_path[MAXPATHLEN];
afd354
 char k5users_path[MAXPATHLEN];
afd354
 char * gb_err = NULL;
afd354
 int quiet = 0;
afd354
+int force_fork = 0;
afd354
 /***********/
afd354
 
afd354
 #define KS_TEMPORARY_CACHE "MEMORY:_ksu"
10fa70
@@ -536,6 +542,23 @@ main (argc, argv)
afd354
                prog_name,target_user,client_name,
afd354
                source_user,ontty());
afd354
 
afd354
+#ifdef USE_PAM
afd354
+        if (appl_pam_enabled(ksu_context, "ksu")) {
afd354
+            if (appl_pam_acct_mgmt(KSU_PAM_SERVICE, 1, target_user, NULL,
afd354
+                                   NULL, source_user,
afd354
+                                   ttyname(STDERR_FILENO)) != 0) {
afd354
+                fprintf(stderr, "Access denied for %s.\n", target_user);
afd354
+                exit(1);
afd354
+            }
afd354
+            if (appl_pam_requires_chauthtok()) {
afd354
+                fprintf(stderr, "Password change required for %s.\n",
afd354
+                        target_user);
afd354
+                exit(1);
afd354
+            }
afd354
+            force_fork++;
afd354
+        }
afd354
+#endif
afd354
+
afd354
         /* Run authorization as target.*/
afd354
         if (krb5_seteuid(target_uid)) {
afd354
             com_err(prog_name, errno, _("while switching to target for "
10fa70
@@ -596,6 +619,24 @@ main (argc, argv)
afd354
 
afd354
             exit(1);
afd354
         }
afd354
+#ifdef USE_PAM
afd354
+    } else {
afd354
+        /* we always do PAM account management, even for root */
afd354
+        if (appl_pam_enabled(ksu_context, "ksu")) {
afd354
+            if (appl_pam_acct_mgmt(KSU_PAM_SERVICE, 1, target_user, NULL,
afd354
+                                   NULL, source_user,
afd354
+                                   ttyname(STDERR_FILENO)) != 0) {
afd354
+                fprintf(stderr, "Access denied for %s.\n", target_user);
afd354
+                exit(1);
afd354
+            }
afd354
+            if (appl_pam_requires_chauthtok()) {
afd354
+                fprintf(stderr, "Password change required for %s.\n",
afd354
+                        target_user);
afd354
+                exit(1);
afd354
+            }
afd354
+            force_fork++;
afd354
+        }
afd354
+#endif
afd354
     }
afd354
 
afd354
     if( some_rest_copy){
10fa70
@@ -653,6 +694,30 @@ main (argc, argv)
afd354
         exit(1);
afd354
     }
afd354
 
afd354
+#ifdef USE_PAM
afd354
+    if (appl_pam_enabled(ksu_context, "ksu")) {
afd354
+        if (appl_pam_session_open() != 0) {
afd354
+            fprintf(stderr, "Error opening session for %s.\n", target_user);
afd354
+            exit(1);
afd354
+        }
afd354
+#ifdef DEBUG
afd354
+        if (auth_debug){
afd354
+            printf(" Opened PAM session.\n");
afd354
+        }
afd354
+#endif
afd354
+        if (appl_pam_cred_init()) {
afd354
+            fprintf(stderr, "Error initializing credentials for %s.\n",
afd354
+                    target_user);
afd354
+            exit(1);
afd354
+        }
afd354
+#ifdef DEBUG
afd354
+        if (auth_debug){
afd354
+            printf(" Initialized PAM credentials.\n");
afd354
+        }
afd354
+#endif
afd354
+    }
afd354
+#endif
afd354
+
afd354
     /* set permissions */
afd354
     if (setgid(target_pwd->pw_gid) < 0) {
afd354
         perror("ksu: setgid");
10fa70
@@ -750,7 +815,7 @@ main (argc, argv)
afd354
         fprintf(stderr, "program to be execed %s\n",params[0]);
afd354
     }
afd354
 
afd354
-    if( keep_target_cache ) {
afd354
+    if( keep_target_cache && !force_fork ) {
afd354
         execv(params[0], params);
afd354
         com_err(prog_name, errno, _("while trying to execv %s"), params[0]);
afd354
         sweep_up(ksu_context, cc_target);
10fa70
@@ -780,16 +845,35 @@ main (argc, argv)
afd354
             if (ret_pid == -1) {
afd354
                 com_err(prog_name, errno, _("while calling waitpid"));
afd354
             }
afd354
-            sweep_up(ksu_context, cc_target);
afd354
+            if( !keep_target_cache ) {
afd354
+                sweep_up(ksu_context, cc_target);
afd354
+            }
afd354
             exit (statusp);
afd354
         case -1:
afd354
             com_err(prog_name, errno, _("while trying to fork."));
afd354
             sweep_up(ksu_context, cc_target);
afd354
             exit (1);
afd354
         case 0:
afd354
+#ifdef USE_PAM
afd354
+            if (appl_pam_enabled(ksu_context, "ksu")) {
afd354
+                if (appl_pam_setenv() != 0) {
afd354
+                    fprintf(stderr, "Error setting up environment for %s.\n",
afd354
+                            target_user);
afd354
+                    exit (1);
afd354
+                }
afd354
+#ifdef DEBUG
afd354
+                if (auth_debug){
afd354
+                    printf(" Set up PAM environment.\n");
afd354
+                }
afd354
+#endif
afd354
+            }
afd354
+#endif
afd354
             execv(params[0], params);
afd354
             com_err(prog_name, errno, _("while trying to execv %s"),
afd354
                     params[0]);
afd354
+            if( keep_target_cache ) {
afd354
+                sweep_up(ksu_context, cc_target);
afd354
+            }
afd354
             exit (1);
afd354
         }
afd354
     }
afd354
diff --git a/src/clients/ksu/pam.c b/src/clients/ksu/pam.c
afd354
new file mode 100644
afd354
index 000000000..cbfe48704
afd354
--- /dev/null
afd354
+++ b/src/clients/ksu/pam.c
afd354
@@ -0,0 +1,389 @@
afd354
+/*
afd354
+ * src/clients/ksu/pam.c
afd354
+ *
afd354
+ * Copyright 2007,2009,2010 Red Hat, Inc.
afd354
+ *
afd354
+ * All Rights Reserved.
afd354
+ *
afd354
+ * Redistribution and use in source and binary forms, with or without
afd354
+ * modification, are permitted provided that the following conditions are met:
afd354
+ *
afd354
+ *  Redistributions of source code must retain the above copyright notice, this
afd354
+ *  list of conditions and the following disclaimer.
afd354
+ *
afd354
+ *  Redistributions in binary form must reproduce the above copyright notice,
afd354
+ *  this list of conditions and the following disclaimer in the documentation
afd354
+ *  and/or other materials provided with the distribution.
afd354
+ *
afd354
+ *  Neither the name of Red Hat, Inc. nor the names of its contributors may be
afd354
+ *  used to endorse or promote products derived from this software without
afd354
+ *  specific prior written permission.
afd354
+ *
afd354
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
afd354
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
afd354
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
afd354
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
afd354
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
afd354
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
afd354
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
afd354
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
afd354
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
afd354
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
afd354
+ * POSSIBILITY OF SUCH DAMAGE.
afd354
+ * 
afd354
+ * Convenience wrappers for using PAM.
afd354
+ */
afd354
+
afd354
+#include "autoconf.h"
afd354
+#ifdef USE_PAM
afd354
+#include <sys/types.h>
afd354
+#include <stdio.h>
afd354
+#include <stdlib.h>
afd354
+#include <string.h>
afd354
+#include <unistd.h>
afd354
+#include "k5-int.h"
afd354
+#include "pam.h"
afd354
+
afd354
+#ifndef MAXPWSIZE
afd354
+#define MAXPWSIZE 128
afd354
+#endif
afd354
+
afd354
+static int appl_pam_started;
afd354
+static pid_t appl_pam_starter = -1;
afd354
+static int appl_pam_session_opened;
afd354
+static int appl_pam_creds_initialized;
afd354
+static int appl_pam_pwchange_required;
afd354
+static pam_handle_t *appl_pamh;
afd354
+static struct pam_conv appl_pam_conv;
afd354
+static char *appl_pam_user;
afd354
+struct appl_pam_non_interactive_args {
afd354
+	const char *user;
afd354
+	const char *password;
afd354
+};
afd354
+
afd354
+int
afd354
+appl_pam_enabled(krb5_context context, const char *section)
afd354
+{
afd354
+	int enabled = 1;
afd354
+	if ((context != NULL) && (context->profile != NULL)) {
afd354
+		if (profile_get_boolean(context->profile,
afd354
+					section,
afd354
+					USE_PAM_CONFIGURATION_KEYWORD,
afd354
+					NULL,
afd354
+					enabled, &enabled) != 0) {
afd354
+			enabled = 1;
afd354
+		}
afd354
+	}
afd354
+	return enabled;
afd354
+}
afd354
+
afd354
+void
afd354
+appl_pam_cleanup(void)
afd354
+{
afd354
+	if (getpid() != appl_pam_starter) {
afd354
+		return;
afd354
+	}
afd354
+#ifdef DEBUG
afd354
+	printf("Called to clean up PAM.\n");
afd354
+#endif
afd354
+	if (appl_pam_creds_initialized) {
afd354
+#ifdef DEBUG
afd354
+		printf("Deleting PAM credentials.\n");
afd354
+#endif
afd354
+		pam_setcred(appl_pamh, PAM_DELETE_CRED);
afd354
+		appl_pam_creds_initialized = 0;
afd354
+	}
afd354
+	if (appl_pam_session_opened) {
afd354
+#ifdef DEBUG
afd354
+		printf("Closing PAM session.\n");
afd354
+#endif
afd354
+		pam_close_session(appl_pamh, 0);
afd354
+		appl_pam_session_opened = 0;
afd354
+	}
afd354
+	appl_pam_pwchange_required = 0;
afd354
+	if (appl_pam_started) {
afd354
+#ifdef DEBUG
afd354
+		printf("Shutting down PAM.\n");
afd354
+#endif
afd354
+		pam_end(appl_pamh, 0);
afd354
+		appl_pam_started = 0;
afd354
+		appl_pam_starter = -1;
afd354
+		free(appl_pam_user);
afd354
+		appl_pam_user = NULL;
afd354
+	}
afd354
+}
afd354
+static int
afd354
+appl_pam_interactive_converse(int num_msg, const struct pam_message **msg,
afd354
+			      struct pam_response **presp, void *appdata_ptr)
afd354
+{
afd354
+	const struct pam_message *message;
afd354
+	struct pam_response *resp;
afd354
+	int i, code;
afd354
+	char *pwstring, pwbuf[MAXPWSIZE];
afd354
+	unsigned int pwsize;
afd354
+	resp = malloc(sizeof(struct pam_response) * num_msg);
afd354
+	if (resp == NULL) {
afd354
+		return PAM_BUF_ERR;
afd354
+	}
afd354
+	memset(resp, 0, sizeof(struct pam_response) * num_msg);
afd354
+	code = PAM_SUCCESS;
afd354
+	for (i = 0; i < num_msg; i++) {
afd354
+		message = &(msg[0][i]); /* XXX */
afd354
+		message = msg[i]; /* XXX */
afd354
+		pwstring = NULL;
afd354
+		switch (message->msg_style) {
afd354
+		case PAM_TEXT_INFO:
afd354
+		case PAM_ERROR_MSG:
afd354
+			printf("[%s]\n", message->msg ? message->msg : "");
afd354
+			fflush(stdout);
afd354
+			resp[i].resp = NULL;
afd354
+			resp[i].resp_retcode = PAM_SUCCESS;
afd354
+			break;
afd354
+		case PAM_PROMPT_ECHO_ON:
afd354
+		case PAM_PROMPT_ECHO_OFF:
afd354
+			if (message->msg_style == PAM_PROMPT_ECHO_ON) {
afd354
+				if (fgets(pwbuf, sizeof(pwbuf),
afd354
+					  stdin) != NULL) {
afd354
+					pwbuf[strcspn(pwbuf, "\r\n")] = '\0';
afd354
+					pwstring = pwbuf;
afd354
+				}
afd354
+			} else {
afd354
+				pwstring = getpass(message->msg ?
afd354
+						   message->msg :
afd354
+						   "");
afd354
+			}
afd354
+			if ((pwstring != NULL) && (pwstring[0] != '\0')) {
afd354
+				pwsize = strlen(pwstring);
afd354
+				resp[i].resp = malloc(pwsize + 1);
afd354
+				if (resp[i].resp == NULL) {
afd354
+					resp[i].resp_retcode = PAM_BUF_ERR;
afd354
+				} else {
afd354
+					memcpy(resp[i].resp, pwstring, pwsize);
afd354
+					resp[i].resp[pwsize] = '\0';
afd354
+					resp[i].resp_retcode = PAM_SUCCESS;
afd354
+				}
afd354
+			} else {
afd354
+				resp[i].resp_retcode = PAM_CONV_ERR;
afd354
+				code = PAM_CONV_ERR;
afd354
+			}
afd354
+			break;
afd354
+		default:
afd354
+			break;
afd354
+		}
afd354
+	}
afd354
+	*presp = resp;
afd354
+	return code;
afd354
+}
afd354
+static int
afd354
+appl_pam_non_interactive_converse(int num_msg,
afd354
+				  const struct pam_message **msg,
afd354
+				  struct pam_response **presp,
afd354
+				  void *appdata_ptr)
afd354
+{
afd354
+	const struct pam_message *message;
afd354
+	struct pam_response *resp;
afd354
+	int i, code;
afd354
+	unsigned int pwsize;
afd354
+	struct appl_pam_non_interactive_args *args;
afd354
+	const char *pwstring;
afd354
+	resp = malloc(sizeof(struct pam_response) * num_msg);
afd354
+	if (resp == NULL) {
afd354
+		return PAM_BUF_ERR;
afd354
+	}
afd354
+	args = appdata_ptr;
afd354
+	memset(resp, 0, sizeof(struct pam_response) * num_msg);
afd354
+	code = PAM_SUCCESS;
afd354
+	for (i = 0; i < num_msg; i++) {
afd354
+		message = &((*msg)[i]);
afd354
+		message = msg[i];
afd354
+		pwstring = NULL;
afd354
+		switch (message->msg_style) {
afd354
+		case PAM_TEXT_INFO:
afd354
+		case PAM_ERROR_MSG:
afd354
+			break;
afd354
+		case PAM_PROMPT_ECHO_ON:
afd354
+		case PAM_PROMPT_ECHO_OFF:
afd354
+			if (message->msg_style == PAM_PROMPT_ECHO_ON) {
afd354
+				/* assume "user" */
afd354
+				pwstring = args->user;
afd354
+			} else {
afd354
+				/* assume "password" */
afd354
+				pwstring = args->password;
afd354
+			}
afd354
+			if ((pwstring != NULL) && (pwstring[0] != '\0')) {
afd354
+				pwsize = strlen(pwstring);
afd354
+				resp[i].resp = malloc(pwsize + 1);
afd354
+				if (resp[i].resp == NULL) {
afd354
+					resp[i].resp_retcode = PAM_BUF_ERR;
afd354
+				} else {
afd354
+					memcpy(resp[i].resp, pwstring, pwsize);
afd354
+					resp[i].resp[pwsize] = '\0';
afd354
+					resp[i].resp_retcode = PAM_SUCCESS;
afd354
+				}
afd354
+			} else {
afd354
+				resp[i].resp_retcode = PAM_CONV_ERR;
afd354
+				code = PAM_CONV_ERR;
afd354
+			}
afd354
+			break;
afd354
+		default:
afd354
+			break;
afd354
+		}
afd354
+	}
afd354
+	*presp = resp;
afd354
+	return code;
afd354
+}
afd354
+static int
afd354
+appl_pam_start(const char *service, int interactive,
afd354
+	       const char *login_username,
afd354
+	       const char *non_interactive_password,
afd354
+	       const char *hostname,
afd354
+	       const char *ruser,
afd354
+	       const char *tty)
afd354
+{
afd354
+	static int exit_handler_registered;
afd354
+	static struct appl_pam_non_interactive_args args;
afd354
+	int ret = 0;
afd354
+	if (appl_pam_started &&
afd354
+	    (strcmp(login_username, appl_pam_user) != 0)) {
afd354
+		appl_pam_cleanup();
afd354
+		appl_pam_user = NULL;
afd354
+	}
afd354
+	if (!appl_pam_started) {
afd354
+#ifdef DEBUG
afd354
+		printf("Starting PAM up (service=\"%s\",user=\"%s\").\n",
afd354
+		       service, login_username);
afd354
+#endif
afd354
+		memset(&appl_pam_conv, 0, sizeof(appl_pam_conv));
afd354
+		appl_pam_conv.conv = interactive ?
afd354
+				     &appl_pam_interactive_converse :
afd354
+				     &appl_pam_non_interactive_converse;
afd354
+		memset(&args, 0, sizeof(args));
afd354
+		args.user = strdup(login_username);
afd354
+		args.password = non_interactive_password ?
afd354
+				strdup(non_interactive_password) :
afd354
+				NULL;
afd354
+		appl_pam_conv.appdata_ptr = &arg;;
afd354
+		ret = pam_start(service, login_username,
afd354
+				&appl_pam_conv, &appl_pamh);
afd354
+		if (ret == 0) {
afd354
+			if (hostname != NULL) {
afd354
+#ifdef DEBUG
afd354
+				printf("Setting PAM_RHOST to \"%s\".\n", hostname);
afd354
+#endif
afd354
+				pam_set_item(appl_pamh, PAM_RHOST, hostname);
afd354
+			}
afd354
+			if (ruser != NULL) {
afd354
+#ifdef DEBUG
afd354
+				printf("Setting PAM_RUSER to \"%s\".\n", ruser);
afd354
+#endif
afd354
+				pam_set_item(appl_pamh, PAM_RUSER, ruser);
afd354
+			}
afd354
+			if (tty != NULL) {
afd354
+#ifdef DEBUG
afd354
+				printf("Setting PAM_TTY to \"%s\".\n", tty);
afd354
+#endif
afd354
+				pam_set_item(appl_pamh, PAM_TTY, tty);
afd354
+			}
afd354
+			if (!exit_handler_registered &&
afd354
+			    (atexit(appl_pam_cleanup) != 0)) {
afd354
+				pam_end(appl_pamh, 0);
afd354
+				appl_pamh = NULL;
afd354
+				ret = -1;
afd354
+			} else {
afd354
+				appl_pam_started = 1;
afd354
+				appl_pam_starter = getpid();
afd354
+				appl_pam_user = strdup(login_username);
afd354
+				exit_handler_registered = 1;
afd354
+			}
afd354
+		}
afd354
+	}
afd354
+	return ret;
afd354
+}
afd354
+int
afd354
+appl_pam_acct_mgmt(const char *service, int interactive,
afd354
+		   const char *login_username,
afd354
+		   const char *non_interactive_password,
afd354
+		   const char *hostname,
afd354
+		   const char *ruser,
afd354
+		   const char *tty)
afd354
+{
afd354
+	int ret;
afd354
+	appl_pam_pwchange_required = 0;
afd354
+	ret = appl_pam_start(service, interactive, login_username,
afd354
+			     non_interactive_password, hostname, ruser, tty);
afd354
+	if (ret == 0) {
afd354
+#ifdef DEBUG
afd354
+		printf("Calling pam_acct_mgmt().\n");
afd354
+#endif
afd354
+		ret = pam_acct_mgmt(appl_pamh, 0);
afd354
+		switch (ret) {
afd354
+		case PAM_IGNORE:
afd354
+			ret = 0;
afd354
+			break;
afd354
+		case PAM_NEW_AUTHTOK_REQD:
afd354
+			appl_pam_pwchange_required = 1;
afd354
+			ret = 0;
afd354
+			break;
afd354
+		default:
afd354
+			break;
afd354
+		}
afd354
+	}
afd354
+	return ret;
afd354
+}
afd354
+int
afd354
+appl_pam_requires_chauthtok(void)
afd354
+{
afd354
+	return appl_pam_pwchange_required;
afd354
+}
afd354
+int
afd354
+appl_pam_session_open(void)
afd354
+{
afd354
+	int ret = 0;
afd354
+	if (appl_pam_started) {
afd354
+#ifdef DEBUG
afd354
+		printf("Opening PAM session.\n");
afd354
+#endif
afd354
+		ret = pam_open_session(appl_pamh, 0);
afd354
+		if (ret == 0) {
afd354
+			appl_pam_session_opened = 1;
afd354
+		}
afd354
+	}
afd354
+	return ret;
afd354
+}
afd354
+int
afd354
+appl_pam_setenv(void)
afd354
+{
afd354
+	int ret = 0;
afd354
+#ifdef HAVE_PAM_GETENVLIST
afd354
+#ifdef HAVE_PUTENV
afd354
+	int i;
afd354
+	char **list;
afd354
+	if (appl_pam_started) {
afd354
+		list = pam_getenvlist(appl_pamh);
afd354
+		for (i = 0; ((list != NULL) && (list[i] != NULL)); i++) {
afd354
+#ifdef DEBUG
afd354
+			printf("Setting \"%s\" in environment.\n", list[i]);
afd354
+#endif
afd354
+			putenv(list[i]);
afd354
+		}
afd354
+	}
afd354
+#endif
afd354
+#endif
afd354
+	return ret;
afd354
+}
afd354
+int
afd354
+appl_pam_cred_init(void)
afd354
+{
afd354
+	int ret = 0;
afd354
+	if (appl_pam_started) {
afd354
+#ifdef DEBUG
afd354
+		printf("Initializing PAM credentials.\n");
afd354
+#endif
afd354
+		ret = pam_setcred(appl_pamh, PAM_ESTABLISH_CRED);
afd354
+		if (ret == 0) {
afd354
+			appl_pam_creds_initialized = 1;
afd354
+		}
afd354
+	}
afd354
+	return ret;
afd354
+}
afd354
+#endif
afd354
diff --git a/src/clients/ksu/pam.h b/src/clients/ksu/pam.h
afd354
new file mode 100644
afd354
index 000000000..0ab76569c
afd354
--- /dev/null
afd354
+++ b/src/clients/ksu/pam.h
afd354
@@ -0,0 +1,57 @@
afd354
+/*
afd354
+ * src/clients/ksu/pam.h
afd354
+ *
afd354
+ * Copyright 2007,2009,2010 Red Hat, Inc.
afd354
+ *
afd354
+ * All Rights Reserved.
afd354
+ *
afd354
+ * Redistribution and use in source and binary forms, with or without
afd354
+ * modification, are permitted provided that the following conditions are met:
afd354
+ *
afd354
+ *  Redistributions of source code must retain the above copyright notice, this
afd354
+ *  list of conditions and the following disclaimer.
afd354
+ *
afd354
+ *  Redistributions in binary form must reproduce the above copyright notice,
afd354
+ *  this list of conditions and the following disclaimer in the documentation
afd354
+ *  and/or other materials provided with the distribution.
afd354
+ *
afd354
+ *  Neither the name of Red Hat, Inc. nor the names of its contributors may be
afd354
+ *  used to endorse or promote products derived from this software without
afd354
+ *  specific prior written permission.
afd354
+ *
afd354
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
afd354
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
afd354
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
afd354
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
afd354
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
afd354
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
afd354
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
afd354
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
afd354
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
afd354
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
afd354
+ * POSSIBILITY OF SUCH DAMAGE.
afd354
+ * 
afd354
+ * Convenience wrappers for using PAM.
afd354
+ */
afd354
+
afd354
+#include <krb5.h>
afd354
+#ifdef HAVE_SECURITY_PAM_APPL_H
afd354
+#include <security/pam_appl.h>
afd354
+#endif
afd354
+
afd354
+#define USE_PAM_CONFIGURATION_KEYWORD "use_pam"
afd354
+
afd354
+#ifdef USE_PAM
afd354
+int appl_pam_enabled(krb5_context context, const char *section);
afd354
+int appl_pam_acct_mgmt(const char *service, int interactive,
afd354
+		       const char *local_username,
afd354
+		       const char *non_interactive_password,
afd354
+		       const char *hostname,
afd354
+		       const char *ruser,
afd354
+		       const char *tty);
afd354
+int appl_pam_requires_chauthtok(void);
afd354
+int appl_pam_session_open(void);
afd354
+int appl_pam_setenv(void);
afd354
+int appl_pam_cred_init(void);
afd354
+void appl_pam_cleanup(void);
afd354
+#endif
10fa70
diff --git a/src/configure.ac b/src/configure.ac
10fa70
index 234f4281c..d1f576124 100644
10fa70
--- a/src/configure.ac
10fa70
+++ b/src/configure.ac
10fa70
@@ -1390,6 +1390,8 @@ AC_SUBST([VERTO_VERSION])
afd354
 
afd354
 AC_PATH_PROG(GROFF, groff)
afd354
 
afd354
+KRB5_WITH_PAM
afd354
+
afd354
 # Make localedir work in autoconf 2.5x.
afd354
 if test "${localedir+set}" != set; then
afd354
     localedir='$(datadir)/locale'