--- sh-utils-2.0.12/src/Makefile.am.pam	Thu Apr 25 20:50:57 2002
+++ sh-utils-2.0.12/src/Makefile.am	Thu Aug 29 21:47:09 2002
@@ -47,7 +47,7 @@
 
 uptime_LDADD = $(LDADD) @GETLOADAVG_LIBS@
 
-su_LDADD = $(LDADD) @LIB_CRYPT@
+su_LDADD = $(LDADD) @LIB_CRYPT@ @LIB_PAM@
 
 $(PROGRAMS): ../lib/libfetish.a
 
--- sh-utils-2.0.12/src/su.c.pam	Thu Aug 29 21:47:09 2002
+++ sh-utils-2.0.12/src/su.c	Thu Aug 29 21:57:55 2002
@@ -38,6 +38,16 @@
    restricts who can su to UID 0 accounts.  RMS considers that to
    be fascist.
 
+#ifdef USE_PAM
+
+   Actually, with PAM, su has nothing to do with whether or not a
+   wheel group is enforced by su.  RMS tries to restrict your access
+   to a su which implements the wheel group, but PAM considers that
+   to be fascist, and gives the user/sysadmin the opportunity to
+   enforce a wheel group by proper editing of /etc/pam.conf
+
+#endif
+
    Options:
    -, -l, --login	Make the subshell a login shell.
 			Unset all environment variables except
@@ -81,6 +91,14 @@
    prototype (returning `int') in <unistd.h>.  */
 #define getusershell _getusershell_sys_proto_
 
+#ifdef USE_PAM
+# include <security/pam_appl.h>
+# include <security/pam_misc.h>
+# include <signal.h>
+# include <sys/wait.h>
+# include <sys/fsuid.h>
+#endif /* USE_PAM */
+
 #include "system.h"
 #include "closeout.h"
 #include "dirname.h"
@@ -151,7 +169,9 @@
 /* The user to become if none is specified.  */
 #define DEFAULT_USER "root"
 
+#ifndef USE_PAM
 char *crypt ();
+#endif
 char *getpass ();
 char *getusershell ();
 void endusershell ();
@@ -159,7 +179,7 @@
 
 extern char **environ;
 
-static void run_shell (const char *, const char *, char **)
+static void run_shell (const char *, const char *, char **, const struct passwd *)
      ATTRIBUTE_NORETURN;
 
 /* The name this program was run with.  */
@@ -272,7 +292,22 @@
 }
 #endif
 
+#ifdef USE_PAM
+static pam_handle_t *pamh = NULL;
+static int retval;
+static struct pam_conv conv = {
+  misc_conv,
+  NULL
+};
+
+#define PAM_BAIL_P if (retval) { \
+  pam_end(pamh, PAM_SUCCESS); \
+  return 0; \
+}
+#endif
+
 /* Ask the user for a password.
+   If PAM is in use, let PAM ask for the password if necessary.
    Return 1 if the user gives the correct password for entry PW,
    0 if not.  Return 1 without asking for a password if run by UID 0
    or if PW has an empty password.  */
@@ -280,6 +315,34 @@
 static int
 correct_password (const struct passwd *pw)
 {
+#ifdef USE_PAM
+  struct passwd *caller;
+  retval = pam_start(PROGRAM_NAME, pw->pw_name, &conv, &pamh);
+  PAM_BAIL_P;
+
+  if (getuid() != 0 && !isatty(0)) {
+	fprintf(stderr, "standard in must be a tty\n");
+	exit(1);
+  }
+
+  caller = getpwuid(getuid());
+  if(caller != NULL && caller->pw_name != NULL) {
+	  retval = pam_set_item(pamh, PAM_RUSER, caller->pw_name);
+	  PAM_BAIL_P;
+  }
+
+  retval = pam_authenticate(pamh, 0);
+  PAM_BAIL_P;
+  retval = pam_acct_mgmt(pamh, 0);
+  if (retval == PAM_NEW_AUTHTOK_REQD) {
+    /* password has expired.  Offer option to change it. */
+    retval = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
+    PAM_BAIL_P;
+  }
+  PAM_BAIL_P;
+  /* must be authenticated if this point was reached */
+  return 1;
+#else /* !USE_PAM */
   char *unencrypted, *encrypted, *correct;
 #if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP
   /* Shadow passwd stuff for SVR3 and maybe other systems.  */
@@ -304,6 +367,7 @@
   encrypted = crypt (unencrypted, correct);
   memset (unencrypted, 0, strlen (unencrypted));
   return strcmp (encrypted, correct) == 0;
+#endif /* !USE_PAM */
 }
 
 /* Update `environ' for the new shell based on PW, with SHELL being
@@ -313,16 +377,24 @@
 modify_environment (const struct passwd *pw, const char *shell)
 {
   char *term;
+  char *display;
+  char *xauthority;
 
   if (simulate_login)
     {
-      /* Leave TERM unchanged.  Set HOME, SHELL, USER, LOGNAME, PATH.
+      /* Leave TERM, DISPLAY, XAUTHORITY unchanged.  Set HOME, SHELL, USER, LOGNAME, PATH.
          Unset all other environment variables.  */
       term = getenv ("TERM");
+      display = getenv ("DISPLAY");
+      xauthority = getenv ("XAUTHORITY");
       environ = (char **) xmalloc (2 * sizeof (char *));
       environ[0] = 0;
       if (term)
 	xputenv (concat ("TERM", "=", term));
+      if (display)
+	xputenv (concat ("DISPLAY", "=", display));
+      if (xauthority)
+	xputenv (concat ("XAUTHORITY", "=", xauthority));
       xputenv (concat ("HOME", "=", pw->pw_dir));
       xputenv (concat ("SHELL", "=", shell));
       xputenv (concat ("USER", "=", pw->pw_name));
@@ -359,23 +431,73 @@
     error (1, errno, _("cannot set groups"));
   endgrent ();
 #endif
+#ifdef USE_PAM
+  retval = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+  if (retval != PAM_SUCCESS)
+    error (1, 0, pam_strerror(pamh, retval));
+#endif /* USE_PAM */
   if (setgid (pw->pw_gid))
     error (1, errno, _("cannot set group id"));
   if (setuid (pw->pw_uid))
     error (1, errno, _("cannot set user id"));
 }
 
+#ifdef USE_PAM
+static int caught=0;
+/* Signal handler for parent process later */
+static void su_catch_sig(int sig)
+{
+  ++caught;
+}
+
+int
+pam_copyenv (pam_handle_t *pamh)
+{
+  char **env;
+
+  env = pam_getenvlist(pamh);
+  if(env) {
+    while(*env) {
+	xputenv(*env);
+	env++;
+    }
+  }
+  return(0);
+}
+#endif
+
 /* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
    If COMMAND is nonzero, pass it to the shell with the -c option.
    If ADDITIONAL_ARGS is nonzero, pass it to the shell as more
    arguments.  */
 
 static void
-run_shell (const char *shell, const char *command, char **additional_args)
+run_shell (const char *shell, const char *command, char **additional_args, const struct passwd *pw)
 {
   const char **args;
   int argno = 1;
+#ifdef USE_PAM
+  int child;
+  sigset_t ourset;
+  int status;
+
+  retval = pam_open_session(pamh,0);
+  if (retval != PAM_SUCCESS) {
+    fprintf (stderr, "could not open session\n");
+    exit (1);
+  }
+
+/* do this at the last possible moment, because environment variables may
+   be passed even in the session phase
+*/
+  if(pam_copyenv(pamh) != PAM_SUCCESS)
+     fprintf (stderr, "error copying PAM environment\n");
 
+  child = fork();
+  if (child == 0) {  /* child shell */
+  change_identity (pw);
+  pam_end(pamh, 0);
+#endif
   if (additional_args)
     args = (const char **) xmalloc (sizeof (char *)
 				    * (10 + elements (additional_args)));
@@ -407,7 +529,62 @@
   args[argno] = NULL;
   execv (shell, (char **) args);
   error (0, errno, _("cannot run %s"), shell);
-  exit (1);
+  exit(1);
+#ifdef USE_PAM
+  } else if (child == -1) {
+      fprintf(stderr, "can not fork user shell: %s", strerror(errno));
+      exit(1);
+  }
+  /* parent only */
+  sigfillset(&ourset);
+  if (sigprocmask(SIG_BLOCK, &ourset, NULL)) {
+    fprintf(stderr, "%s: signal malfunction\n", PROGRAM_NAME);
+    caught = 1;
+  }
+  if (!caught) {
+    struct sigaction action;
+    action.sa_handler = su_catch_sig;
+    sigemptyset(&action.sa_mask);
+    action.sa_flags = 0;
+    sigemptyset(&ourset);
+    if (sigaddset(&ourset, SIGTERM)
+        || sigaddset(&ourset, SIGALRM)
+        || sigaction(SIGTERM, &action, NULL)
+        || sigprocmask(SIG_UNBLOCK, &ourset, NULL)) {
+      fprintf(stderr, "%s: signal masking malfunction\n", PROGRAM_NAME);
+      caught = 1;
+    }
+  }
+  if (!caught) {
+    do {
+      int pid;
+
+      pid = waitpid(-1, &status, WUNTRACED);
+
+      if (WIFSTOPPED(status)) {
+          kill(getpid(), SIGSTOP);
+          /* once we get here, we must have resumed */
+          kill(pid, SIGCONT);
+      }
+    } while (WIFSTOPPED(status));
+  }
+
+  if (caught) {
+    fprintf(stderr, "\nSession terminated, killing shell...");
+    kill (child, SIGTERM);
+  }
+  retval = pam_close_session(pamh, 0);
+  PAM_BAIL_P;
+  retval = pam_end(pamh, PAM_SUCCESS);
+  PAM_BAIL_P;
+  if (caught) {
+    sleep(2);
+    kill(child, SIGKILL);
+    fprintf(stderr, " ...killed.\n");
+    exit(-1);
+  }
+  exit (WEXITSTATUS(status));
+#endif /* USE_PAM */
 }
 
 /* Return 1 if SHELL is a restricted shell (one not returned by
@@ -580,9 +757,17 @@
     }
   modify_environment (pw, shell);
 
+#ifdef USE_PAM
+  setfsgid(pw->pw_gid);
+  setfsuid(pw->pw_uid);
+  if (initgroups (pw->pw_name, pw->pw_gid) == -1)
+    error (1, errno, _("cannot set groups"));
+  endgrent ();
+#else
   change_identity (pw);
+#endif
   if (simulate_login && chdir (pw->pw_dir))
     error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir);
 
-  run_shell (shell, command, additional_args);
+  run_shell (shell, command, additional_args, pw);
 }
--- sh-utils-2.0.12/configure.ac.pam	Sun Apr 28 11:29:18 2002
+++ sh-utils-2.0.12/configure.ac	Thu Aug 29 21:47:09 2002
@@ -8,6 +8,13 @@
 
 AM_INIT_AUTOMAKE([1.6 gnits dist-bzip2])
 
+dnl Give the chance to enable PAM
+AC_ARG_ENABLE(pam, dnl
+[  --enable-pam              Enable use of the PAM libraries],
+[AC_DEFINE(USE_PAM, 1, [Define if you want to use PAM])
+LIB_PAM="-ldl -lpam -lpam_misc"
+AC_SUBST(LIB_PAM)])
+
 jm_PERL
 
 AC_PROG_CC
