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

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