summary refs log tree commit diff
path: root/pkgs/tools
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2007-01-16 22:24:03 +0000
committerEelco Dolstra <eelco.dolstra@logicblox.com>2007-01-16 22:24:03 +0000
commita71003612befcf27b1a0b81855f8e9ca1baa0fe0 (patch)
tree6cbf6742a36ddb257f26b54c5587429570d55538 /pkgs/tools
parent4d2519baad5b50536fc99146971942f40f9af3bf (diff)
downloadnixlib-a71003612befcf27b1a0b81855f8e9ca1baa0fe0.tar
nixlib-a71003612befcf27b1a0b81855f8e9ca1baa0fe0.tar.gz
nixlib-a71003612befcf27b1a0b81855f8e9ca1baa0fe0.tar.bz2
nixlib-a71003612befcf27b1a0b81855f8e9ca1baa0fe0.tar.lz
nixlib-a71003612befcf27b1a0b81855f8e9ca1baa0fe0.tar.xz
nixlib-a71003612befcf27b1a0b81855f8e9ca1baa0fe0.tar.zst
nixlib-a71003612befcf27b1a0b81855f8e9ca1baa0fe0.zip
* Merge SUSE's coreutils-5.0-pam-env.patch so that PAM modules can
  pass environment variables (notably DISPLAY and XAUTHORITY) to the
  child shell.

svn path=/nixpkgs/trunk/; revision=7701
Diffstat (limited to 'pkgs/tools')
-rw-r--r--pkgs/tools/misc/su/default.nix1
-rw-r--r--pkgs/tools/misc/su/su-pam.patch1640
2 files changed, 1018 insertions, 623 deletions
diff --git a/pkgs/tools/misc/su/default.nix b/pkgs/tools/misc/su/default.nix
index 825005d07a38..1cfc80376560 100644
--- a/pkgs/tools/misc/su/default.nix
+++ b/pkgs/tools/misc/su/default.nix
@@ -14,7 +14,6 @@ stdenv.mkDerivation {
     # PAM patch taken from SUSE's coreutils-6.7-5.src.rpm.
     ./su-pam.patch
   ];
-  patchFlags = "-p0";
   buildInputs = [pam];
   buildPhase = "
     make -C lib
diff --git a/pkgs/tools/misc/su/su-pam.patch b/pkgs/tools/misc/su/su-pam.patch
index 6bd4ea2c5e86..3f81a47f12dc 100644
--- a/pkgs/tools/misc/su/su-pam.patch
+++ b/pkgs/tools/misc/su/su-pam.patch
@@ -1,622 +1,1018 @@
---- src/getdef.c
-+++ src/getdef.c
-@@ -0,0 +1,257 @@
-+/* Copyright (C) 2003, 2004, 2005 Thorsten Kukuk
-+   Author: Thorsten Kukuk <kukuk@suse.de>
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License version 2 as
-+   published by the Free Software Foundation.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program; if not, write to the Free Software Foundation,
-+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
-+
-+#ifdef HAVE_CONFIG_H
-+#include <config.h>
-+#endif
-+
-+#define _GNU_SOURCE
-+
-+#include <errno.h>
-+#include <ctype.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <limits.h>
-+
-+#include "getdef.h"
-+
-+struct item {
-+  char *name;         /* name of the option.  */
-+  char *value;        /* value of the option.  */
-+  struct item *next;  /* pointer to next option.  */
-+};
-+
-+static struct item *list = NULL;
-+
-+void
-+free_getdef_data (void)
-+{
-+  struct item *ptr;
-+
-+  ptr = list;
-+  while (ptr != NULL)
-+    {
-+      struct item *tmp;
-+      tmp = ptr->next;
-+      free (ptr->name);
-+      free (ptr->value);
-+      free (ptr);
-+      ptr = tmp;
-+    }
-+
-+  list = NULL;
-+}
-+
-+/* Add a new entry to the list.  */
-+static void
-+store (const char *name, const char *value)
-+{
-+  struct item *new = malloc (sizeof (struct item));
-+
-+  if (new == NULL)
-+    abort ();
-+
-+  if (name == NULL)
-+    abort ();
-+
-+  new->name = strdup (name);
-+  new->value = strdup (value?:"");
-+  new->next = list;
-+  list = new;
-+}
-+
-+/* search a special entry in the list and return the value.  */
-+static const char *
-+search (const char *name)
-+{
-+  struct item *ptr;
-+
-+  ptr = list;
-+  while (ptr != NULL)
-+    {
-+      if (strcasecmp (name, ptr->name) == 0)
-+	return ptr->value;
-+      ptr = ptr->next;
-+    }
-+
-+  return NULL;
-+}
-+
-+/* Load the login.defs file (/etc/login.defs)  */
-+static void
-+load_defaults_internal (const char *filename)
-+{
-+  FILE *fp;
-+  char *buf = NULL;
-+  size_t buflen = 0;
-+
-+  fp = fopen (filename, "r");
-+  if (NULL == fp)
-+    return;
-+
-+  while (!feof (fp))
-+    {
-+      char *tmp, *cp;
-+#if defined(HAVE_GETLINE)
-+      ssize_t n = getline (&buf, &buflen, fp);
-+#elif defined (HAVE_GETDELIM)
-+      ssize_t n = getdelim (&buf, &buflen, '\n', fp);
-+#else
-+      ssize_t n;
-+
-+      if (buf == NULL)
-+        {
-+          buflen = 8096;
-+          buf = malloc (buflen);
-+        }
-+      buf[0] = '\0';
-+      fgets (buf, buflen - 1, fp);
-+      if (buf != NULL)
-+        n = strlen (buf);
-+      else
-+        n = 0;
-+#endif /* HAVE_GETLINE / HAVE_GETDELIM */
-+      cp = buf;
-+
-+      if (n < 1)
-+        break;
-+
-+      tmp = strchr (cp, '#');  /* remove comments */
-+      if (tmp)
-+        *tmp = '\0';
-+      while (isspace ((int)*cp))    /* remove spaces and tabs */
-+        ++cp;
-+      if (*cp == '\0')        /* ignore empty lines */
-+        continue;
-+
-+      if (cp[strlen (cp) - 1] == '\n')
-+        cp[strlen (cp) - 1] = '\0';
-+
-+      tmp = strsep (&cp, " \t=");
-+      if (cp != NULL)
-+	while (isspace ((int)*cp) || *cp == '=')
-+	  ++cp;
-+
-+      store (tmp, cp);
-+    }
-+  fclose (fp);
-+
-+  if (buf)
-+    free (buf);
-+}
-+
-+static void
-+load_defaults (void)
-+{
-+  load_defaults_internal ("/etc/default/su");
-+  load_defaults_internal ("/etc/login.defs");
-+}
-+
-+int
-+getdef_bool (const char *name, int dflt)
-+{
-+  const char *val;
-+
-+  if (list == NULL)
-+    load_defaults ();
-+
-+  val = search (name);
-+
-+  if (val == NULL)
-+    return dflt;
-+
-+  return (strcasecmp (val, "yes") == 0);
-+}
-+
-+long
-+getdef_num (const char *name, long dflt)
-+{
-+  const char *val;
-+  char *cp;
-+  long retval;
-+
-+  if (list == NULL)
-+    load_defaults ();
-+
-+  val = search (name);
-+
-+  if (val == NULL)
-+    return dflt;
-+
-+  retval = strtol (val, &cp, 0);
-+  if (*cp != '\0' ||
-+      ((retval == LONG_MAX || retval == LONG_MIN) && errno == ERANGE))
-+    {
-+      fprintf (stderr,
-+	       "%s contains invalid numerical value: %s!\n",
-+	       name, val);
-+      retval = dflt;
-+    }
-+  return retval;
-+}
-+
-+unsigned long
-+getdef_unum (const char *name, unsigned long dflt)
-+{
-+  const char *val;
-+  char *cp;
-+  unsigned long retval;
-+
-+  if (list == NULL)
-+    load_defaults ();
-+
-+  val = search (name);
-+
-+  if (val == NULL)
-+    return dflt;
-+
-+  retval = strtoul (val, &cp, 0);
-+  if (*cp != '\0' || (retval == ULONG_MAX && errno == ERANGE))
-+    {
-+      fprintf (stderr,
-+	       "%s contains invalid numerical value: %s!\n",
-+	       name, val);
-+      retval = dflt;
-+    }
-+  return retval;
-+}
-+
-+const char *
-+getdef_str (const char *name, const char *dflt)
-+{
-+  const char *retval;
-+
-+  if (list == NULL)
-+    load_defaults ();
-+
-+  retval = search (name);
-+
-+  return retval ?: dflt;
-+}
-+
-+#if defined(TEST)
-+
-+int
-+main ()
-+{
-+  printf ("CYPT=%s\n", getdef_str ("cRypt", "no"));
-+  printf ("LOG_UNKFAIL_ENAB=%s\n", getdef_str ("log_unkfail_enab",""));
-+  printf ("DOESNOTEXIST=%s\n", getdef_str ("DOESNOTEXIST","yes"));
-+  return 0;
-+}
-+
-+#endif
---- src/getdef.h
-+++ src/getdef.h
-@@ -0,0 +1,29 @@
-+/* Copyright (C) 2003, 2005 Thorsten Kukuk
-+   Author: Thorsten Kukuk <kukuk@suse.de>
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License version 2 as
-+   published by the Free Software Foundation.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program; if not, write to the Free Software Foundation,
-+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
-+
-+#ifndef _GETDEF_H_
-+
-+#define _GETDEF_H_ 1
-+
-+extern int getdef_bool (const char *name, int dflt);
-+extern long getdef_num (const char *name, long dflt);
-+extern unsigned long getdef_unum (const char *name, unsigned long dflt);
-+extern const char *getdef_str (const char *name, const char *dflt);
-+
-+/* Free all data allocated by getdef_* calls before.  */
-+extern void free_getdef_data (void);
-+
-+#endif /* _GETDEF_H_ */
---- src/su.c
-+++ src/su.c
-@@ -38,6 +38,12 @@
-    restricts who can su to UID 0 accounts.  RMS considers that to
-    be fascist.
- 
-+   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.d/su
-+
-    Compile-time options:
-    -DSYSLOG_SUCCESS	Log successful su's (by default, to root) with syslog.
-    -DSYSLOG_FAILURE	Log failed su's (by default, to root) with syslog.
-@@ -53,6 +59,13 @@
- #include <sys/types.h>
- #include <pwd.h>
- #include <grp.h>
-+#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
- 
- /* Hide any system prototype for getusershell.
-    This is necessary because some Cray systems have a conflicting
-@@ -66,6 +79,9 @@
- 
- #if HAVE_SYSLOG_H && HAVE_SYSLOG
- # include <syslog.h>
-+# define SYSLOG_SUCCESS  1
-+# define SYSLOG_FAILURE  1
-+# define SYSLOG_NON_ROOT 1
- #else
- # undef SYSLOG_SUCCESS
- # undef SYSLOG_FAILURE
-@@ -99,19 +115,13 @@
- # include <paths.h>
- #endif
- 
-+#include "getdef.h"
-+
- /* The default PATH for simulated logins to non-superuser accounts.  */
--#ifdef _PATH_DEFPATH
--# define DEFAULT_LOGIN_PATH _PATH_DEFPATH
--#else
--# define DEFAULT_LOGIN_PATH ":/usr/ucb:/bin:/usr/bin"
--#endif
-+#define DEFAULT_LOGIN_PATH "/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin"
- 
- /* The default PATH for simulated logins to superuser accounts.  */
--#ifdef _PATH_DEFPATH_ROOT
--# define DEFAULT_ROOT_LOGIN_PATH _PATH_DEFPATH_ROOT
--#else
--# define DEFAULT_ROOT_LOGIN_PATH "/usr/ucb:/bin:/usr/bin:/etc"
--#endif
-+#define DEFAULT_ROOT_LOGIN_PATH "/usr/sbin:/bin:/usr/bin:/sbin:/usr/X11R6/bin"
- 
- /* The shell to run if none is given in the user's passwd entry.  */
- #define DEFAULT_SHELL "/bin/sh"
-@@ -119,7 +129,9 @@
- /* The user to become if none is specified.  */
- #define DEFAULT_USER "root"
- 
-+#ifndef USE_PAM
- char *crypt ();
-+#endif
- char *getusershell ();
- void endusershell ();
- void setusershell ();
-@@ -216,7 +228,26 @@
- }
- #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(a) \
-+  if (retval) \
-+    { \
-+      pam_end (pamh, PAM_SUCCESS); \
-+      a; \
-+    }
-+#endif
-+
- /* Ask the user for a password.
-+   If PAM is in use, let PAM ask for the password if necessary.
-    Return true if the user gives the correct password for entry PW,
-    false if not.  Return true without asking for a password if run by UID 0
-    or if PW has an empty password.  */
-@@ -224,10 +255,49 @@
- static bool
- correct_password (const struct passwd *pw)
- {
-+#ifdef USE_PAM
-+  const struct passwd *lpw;
-+  const char *cp;
-+
-+  retval = pam_start ("su", pw->pw_name, &conv, &pamh);
-+  PAM_BAIL_P (return false);
-+
-+  if (isatty (0) && (cp = ttyname (0)) != NULL)
-+    {
-+      const char *tty;
-+
-+      if (strncmp (cp, "/dev/", 5) == 0)
-+	tty = cp + 5;
-+      else
-+	tty = cp;
-+      retval = pam_set_item (pamh, PAM_TTY, tty);
-+      PAM_BAIL_P (return false);
-+    }
-+  cp = getlogin ();
-+  if (!(cp && *cp && (lpw = getpwnam (cp)) != NULL && lpw->pw_uid == getuid ()))
-+    lpw = getpwuid (getuid ());
-+  if (lpw)
-+    {
-+      retval = pam_set_item (pamh, PAM_RUSER, (const void *) lpw->pw_name);
-+      PAM_BAIL_P (return false);
-+    }
-+  retval = pam_authenticate (pamh, 0);
-+  PAM_BAIL_P (return false);
-+  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 (return false);
-+    }
-+  PAM_BAIL_P (return false);
-+  /* must be authenticated if this point was reached */
-+  return true;
-+#else /* !USE_PAM */
-   char *unencrypted, *encrypted, *correct;
- #if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP
-   /* Shadow passwd stuff for SVR3 and maybe other systems.  */
--  struct spwd *sp = getspnam (pw->pw_name);
-+  const struct spwd *sp = getspnam (pw->pw_name);
- 
-   endspent ();
-   if (sp)
-@@ -248,6 +318,7 @@
-   encrypted = crypt (unencrypted, correct);
-   memset (unencrypted, 0, strlen (unencrypted));
-   return STREQ (encrypted, correct);
-+#endif /* !USE_PAM */
- }
- 
- /* Update `environ' for the new shell based on PW, with SHELL being
-@@ -272,8 +343,8 @@
-       xsetenv ("USER", pw->pw_name);
-       xsetenv ("LOGNAME", pw->pw_name);
-       xsetenv ("PATH", (pw->pw_uid
--			? DEFAULT_LOGIN_PATH
--			: DEFAULT_ROOT_LOGIN_PATH));
-+			? getdef_str ("PATH", DEFAULT_LOGIN_PATH)
-+			: getdef_str ("SUPATH", DEFAULT_ROOT_LOGIN_PATH)));
-     }
-   else
-     {
-@@ -283,6 +354,12 @@
- 	{
- 	  xsetenv ("HOME", pw->pw_dir);
- 	  xsetenv ("SHELL", shell);
-+	  if (getdef_bool ("ALWAYS_SET_PATH", 0))
-+	    xsetenv ("PATH", (pw->pw_uid
-+			      ? getdef_str ("PATH",
-+					    DEFAULT_LOGIN_PATH)
-+			      : getdef_str ("SUPATH",
-+					    DEFAULT_ROOT_LOGIN_PATH)));
- 	  if (pw->pw_uid)
- 	    {
- 	      xsetenv ("USER", pw->pw_name);
-@@ -303,12 +380,35 @@
-     error (EXIT_FAIL, errno, _("cannot set groups"));
-   endgrent ();
- #endif
-+#ifdef USE_PAM
-+  retval = pam_setcred (pamh, PAM_ESTABLISH_CRED);
-+  if (retval != PAM_SUCCESS)
-+    error (EXIT_FAIL, 0, "%s", pam_strerror (pamh, retval));
-+
-+  retval = pam_open_session (pamh,0);
-+  if (retval != PAM_SUCCESS)
-+    {
-+       pam_setcred (pamh, PAM_DELETE_CRED);
-+       error (EXIT_FAIL, 0, "could not open session: %s",
-+              pam_strerror (pamh, retval));
-+    }
-+#endif /* USE_PAM */
-   if (setgid (pw->pw_gid))
-     error (EXIT_FAIL, errno, _("cannot set group id"));
-   if (setuid (pw->pw_uid))
-     error (EXIT_FAIL, errno, _("cannot set user id"));
- }
- 
-+#ifdef USE_PAM
-+static bool caught = false;
-+/* Signal handler for parent process later */
-+static void
-+su_catch_sig (int sig)
-+{
-+  caught = true;
-+}
-+#endif
-+
- /* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
-    If COMMAND is nonzero, pass it to the shell with the -c option.
-    Pass ADDITIONAL_ARGS to the shell as more arguments; there
-@@ -321,6 +421,88 @@
-   size_t n_args = 1 + fast_startup + 2 * !!command + n_additional_args + 1;
-   char const **args = xnmalloc (n_args, sizeof *args);
-   size_t argno = 1;
-+#ifdef USE_PAM
-+  pid_t child;
-+  sigset_t ourset;
-+  int status;
-+
-+  child = fork ();
-+  if (child == (pid_t) -1)
-+    error (EXIT_FAILURE, errno, "cannot fork");
-+
-+  if (child != 0)
-+    {
-+      /* parent only */
-+      sigfillset (&ourset);
-+      if (sigprocmask (SIG_BLOCK, &ourset, NULL))
-+	{
-+	  error (0, errno, "cannot block signals");
-+	  caught = true;
-+	}
-+      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))
-+	    {
-+	      error (0, errno, "cannot set signal handler");
-+	      caught = true;
-+	    }
-+	}
-+      if (!caught)
-+	{
-+	  for (;;)
-+	    {
-+	      pid_t pid;
-+
-+	      pid = waitpid (child, &status, WUNTRACED);
-+
-+	      if (WIFSTOPPED (status))
-+		{
-+		  kill (getpid (), SIGSTOP);
-+		  /* once we get here, we must have resumed */
-+		  kill (pid, SIGCONT);
-+		}
-+	      else
-+		break;
-+	    }
-+	  if (WIFSIGNALED (status))
-+	    status = WTERMSIG (status) + 128;
-+	  else
-+	    status = WEXITSTATUS (status);
-+	}
-+      else
-+	status = 1;
-+
-+      if (caught)
-+	{
-+	  fprintf (stderr, "\nSession terminated, killing shell...");
-+	  kill (child, SIGTERM);
-+	}
-+      retval = pam_setcred (pamh, PAM_DELETE_CRED);
-+      PAM_BAIL_P (exit (EXIT_FAILURE));
-+      retval = pam_close_session (pamh, 0);
-+      PAM_BAIL_P (exit (EXIT_FAILURE));
-+      retval = pam_end (pamh, PAM_SUCCESS);
-+      PAM_BAIL_P (exit (EXIT_FAILURE));
-+      if (caught)
-+	{
-+	  sleep (2);
-+	  kill (child, SIGKILL);
-+	  fprintf (stderr, " ...killed.\n");
-+	}
-+      exit (status);
-+    }
-+
-+  /* child shell */
-+  pam_end (pamh, 0);
-+#endif
- 
-   if (simulate_login)
-     {
-@@ -339,6 +521,11 @@
-     args[argno++] = "-f";
-   if (command)
-     {
-+      if (simulate_login)
-+	/* Bash 2.0 have to be invoked as `-su'.  See the comments in
-+	   `shell.c (run_startup_files)'.  */
-+	args[0] = "-su";
-+
-       args[argno++] = "-c";
-       args[argno++] = command;
-     }
-@@ -495,6 +682,9 @@
- #ifdef SYSLOG_FAILURE
-       log_su (pw, false);
- #endif
-+#ifdef USE_PAM
-+      sleep (getdef_num ("FAIL_DELAY", 1));
-+#endif
-       error (EXIT_FAIL, 0, _("incorrect password"));
-     }
- #ifdef SYSLOG_SUCCESS
+diff -rcN coreutils-6.7-orig/getdef.c coreutils-6.7/getdef.c
+*** coreutils-6.7-orig/getdef.c	Thu Jan  1 00:00:00 1970
+--- coreutils-6.7/getdef.c	Tue Jan 16 22:18:41 2007
+***************
+*** 0 ****
+--- 1,257 ----
++ /* Copyright (C) 2003, 2004, 2005 Thorsten Kukuk
++    Author: Thorsten Kukuk <kukuk@suse.de>
++ 
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License version 2 as
++    published by the Free Software Foundation.
++ 
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++ 
++    You should have received a copy of the GNU General Public License
++    along with this program; if not, write to the Free Software Foundation,
++    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
++ 
++ #ifdef HAVE_CONFIG_H
++ #include <config.h>
++ #endif
++ 
++ #define _GNU_SOURCE
++ 
++ #include <errno.h>
++ #include <ctype.h>
++ #include <stdio.h>
++ #include <stdlib.h>
++ #include <string.h>
++ #include <limits.h>
++ 
++ #include "getdef.h"
++ 
++ struct item {
++   char *name;         /* name of the option.  */
++   char *value;        /* value of the option.  */
++   struct item *next;  /* pointer to next option.  */
++ };
++ 
++ static struct item *list = NULL;
++ 
++ void
++ free_getdef_data (void)
++ {
++   struct item *ptr;
++ 
++   ptr = list;
++   while (ptr != NULL)
++     {
++       struct item *tmp;
++       tmp = ptr->next;
++       free (ptr->name);
++       free (ptr->value);
++       free (ptr);
++       ptr = tmp;
++     }
++ 
++   list = NULL;
++ }
++ 
++ /* Add a new entry to the list.  */
++ static void
++ store (const char *name, const char *value)
++ {
++   struct item *new = malloc (sizeof (struct item));
++ 
++   if (new == NULL)
++     abort ();
++ 
++   if (name == NULL)
++     abort ();
++ 
++   new->name = strdup (name);
++   new->value = strdup (value?:"");
++   new->next = list;
++   list = new;
++ }
++ 
++ /* search a special entry in the list and return the value.  */
++ static const char *
++ search (const char *name)
++ {
++   struct item *ptr;
++ 
++   ptr = list;
++   while (ptr != NULL)
++     {
++       if (strcasecmp (name, ptr->name) == 0)
++ 	return ptr->value;
++       ptr = ptr->next;
++     }
++ 
++   return NULL;
++ }
++ 
++ /* Load the login.defs file (/etc/login.defs)  */
++ static void
++ load_defaults_internal (const char *filename)
++ {
++   FILE *fp;
++   char *buf = NULL;
++   size_t buflen = 0;
++ 
++   fp = fopen (filename, "r");
++   if (NULL == fp)
++     return;
++ 
++   while (!feof (fp))
++     {
++       char *tmp, *cp;
++ #if defined(HAVE_GETLINE)
++       ssize_t n = getline (&buf, &buflen, fp);
++ #elif defined (HAVE_GETDELIM)
++       ssize_t n = getdelim (&buf, &buflen, '\n', fp);
++ #else
++       ssize_t n;
++ 
++       if (buf == NULL)
++         {
++           buflen = 8096;
++           buf = malloc (buflen);
++         }
++       buf[0] = '\0';
++       fgets (buf, buflen - 1, fp);
++       if (buf != NULL)
++         n = strlen (buf);
++       else
++         n = 0;
++ #endif /* HAVE_GETLINE / HAVE_GETDELIM */
++       cp = buf;
++ 
++       if (n < 1)
++         break;
++ 
++       tmp = strchr (cp, '#');  /* remove comments */
++       if (tmp)
++         *tmp = '\0';
++       while (isspace ((int)*cp))    /* remove spaces and tabs */
++         ++cp;
++       if (*cp == '\0')        /* ignore empty lines */
++         continue;
++ 
++       if (cp[strlen (cp) - 1] == '\n')
++         cp[strlen (cp) - 1] = '\0';
++ 
++       tmp = strsep (&cp, " \t=");
++       if (cp != NULL)
++ 	while (isspace ((int)*cp) || *cp == '=')
++ 	  ++cp;
++ 
++       store (tmp, cp);
++     }
++   fclose (fp);
++ 
++   if (buf)
++     free (buf);
++ }
++ 
++ static void
++ load_defaults (void)
++ {
++   load_defaults_internal ("/etc/default/su");
++   load_defaults_internal ("/etc/login.defs");
++ }
++ 
++ int
++ getdef_bool (const char *name, int dflt)
++ {
++   const char *val;
++ 
++   if (list == NULL)
++     load_defaults ();
++ 
++   val = search (name);
++ 
++   if (val == NULL)
++     return dflt;
++ 
++   return (strcasecmp (val, "yes") == 0);
++ }
++ 
++ long
++ getdef_num (const char *name, long dflt)
++ {
++   const char *val;
++   char *cp;
++   long retval;
++ 
++   if (list == NULL)
++     load_defaults ();
++ 
++   val = search (name);
++ 
++   if (val == NULL)
++     return dflt;
++ 
++   retval = strtol (val, &cp, 0);
++   if (*cp != '\0' ||
++       ((retval == LONG_MAX || retval == LONG_MIN) && errno == ERANGE))
++     {
++       fprintf (stderr,
++ 	       "%s contains invalid numerical value: %s!\n",
++ 	       name, val);
++       retval = dflt;
++     }
++   return retval;
++ }
++ 
++ unsigned long
++ getdef_unum (const char *name, unsigned long dflt)
++ {
++   const char *val;
++   char *cp;
++   unsigned long retval;
++ 
++   if (list == NULL)
++     load_defaults ();
++ 
++   val = search (name);
++ 
++   if (val == NULL)
++     return dflt;
++ 
++   retval = strtoul (val, &cp, 0);
++   if (*cp != '\0' || (retval == ULONG_MAX && errno == ERANGE))
++     {
++       fprintf (stderr,
++ 	       "%s contains invalid numerical value: %s!\n",
++ 	       name, val);
++       retval = dflt;
++     }
++   return retval;
++ }
++ 
++ const char *
++ getdef_str (const char *name, const char *dflt)
++ {
++   const char *retval;
++ 
++   if (list == NULL)
++     load_defaults ();
++ 
++   retval = search (name);
++ 
++   return retval ?: dflt;
++ }
++ 
++ #if defined(TEST)
++ 
++ int
++ main ()
++ {
++   printf ("CYPT=%s\n", getdef_str ("cRypt", "no"));
++   printf ("LOG_UNKFAIL_ENAB=%s\n", getdef_str ("log_unkfail_enab",""));
++   printf ("DOESNOTEXIST=%s\n", getdef_str ("DOESNOTEXIST","yes"));
++   return 0;
++ }
++ 
++ #endif
+diff -rcN coreutils-6.7-orig/getdef.h coreutils-6.7/getdef.h
+*** coreutils-6.7-orig/getdef.h	Thu Jan  1 00:00:00 1970
+--- coreutils-6.7/getdef.h	Tue Jan 16 22:18:41 2007
+***************
+*** 0 ****
+--- 1,29 ----
++ /* Copyright (C) 2003, 2005 Thorsten Kukuk
++    Author: Thorsten Kukuk <kukuk@suse.de>
++ 
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License version 2 as
++    published by the Free Software Foundation.
++ 
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++ 
++    You should have received a copy of the GNU General Public License
++    along with this program; if not, write to the Free Software Foundation,
++    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
++ 
++ #ifndef _GETDEF_H_
++ 
++ #define _GETDEF_H_ 1
++ 
++ extern int getdef_bool (const char *name, int dflt);
++ extern long getdef_num (const char *name, long dflt);
++ extern unsigned long getdef_unum (const char *name, unsigned long dflt);
++ extern const char *getdef_str (const char *name, const char *dflt);
++ 
++ /* Free all data allocated by getdef_* calls before.  */
++ extern void free_getdef_data (void);
++ 
++ #endif /* _GETDEF_H_ */
+diff -rcN coreutils-6.7-orig/src/getdef.c coreutils-6.7/src/getdef.c
+*** coreutils-6.7-orig/src/getdef.c	Thu Jan  1 00:00:00 1970
+--- coreutils-6.7/src/getdef.c	Tue Jan 16 22:18:57 2007
+***************
+*** 0 ****
+--- 1,257 ----
++ /* Copyright (C) 2003, 2004, 2005 Thorsten Kukuk
++    Author: Thorsten Kukuk <kukuk@suse.de>
++ 
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License version 2 as
++    published by the Free Software Foundation.
++ 
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++ 
++    You should have received a copy of the GNU General Public License
++    along with this program; if not, write to the Free Software Foundation,
++    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
++ 
++ #ifdef HAVE_CONFIG_H
++ #include <config.h>
++ #endif
++ 
++ #define _GNU_SOURCE
++ 
++ #include <errno.h>
++ #include <ctype.h>
++ #include <stdio.h>
++ #include <stdlib.h>
++ #include <string.h>
++ #include <limits.h>
++ 
++ #include "getdef.h"
++ 
++ struct item {
++   char *name;         /* name of the option.  */
++   char *value;        /* value of the option.  */
++   struct item *next;  /* pointer to next option.  */
++ };
++ 
++ static struct item *list = NULL;
++ 
++ void
++ free_getdef_data (void)
++ {
++   struct item *ptr;
++ 
++   ptr = list;
++   while (ptr != NULL)
++     {
++       struct item *tmp;
++       tmp = ptr->next;
++       free (ptr->name);
++       free (ptr->value);
++       free (ptr);
++       ptr = tmp;
++     }
++ 
++   list = NULL;
++ }
++ 
++ /* Add a new entry to the list.  */
++ static void
++ store (const char *name, const char *value)
++ {
++   struct item *new = malloc (sizeof (struct item));
++ 
++   if (new == NULL)
++     abort ();
++ 
++   if (name == NULL)
++     abort ();
++ 
++   new->name = strdup (name);
++   new->value = strdup (value?:"");
++   new->next = list;
++   list = new;
++ }
++ 
++ /* search a special entry in the list and return the value.  */
++ static const char *
++ search (const char *name)
++ {
++   struct item *ptr;
++ 
++   ptr = list;
++   while (ptr != NULL)
++     {
++       if (strcasecmp (name, ptr->name) == 0)
++ 	return ptr->value;
++       ptr = ptr->next;
++     }
++ 
++   return NULL;
++ }
++ 
++ /* Load the login.defs file (/etc/login.defs)  */
++ static void
++ load_defaults_internal (const char *filename)
++ {
++   FILE *fp;
++   char *buf = NULL;
++   size_t buflen = 0;
++ 
++   fp = fopen (filename, "r");
++   if (NULL == fp)
++     return;
++ 
++   while (!feof (fp))
++     {
++       char *tmp, *cp;
++ #if defined(HAVE_GETLINE)
++       ssize_t n = getline (&buf, &buflen, fp);
++ #elif defined (HAVE_GETDELIM)
++       ssize_t n = getdelim (&buf, &buflen, '\n', fp);
++ #else
++       ssize_t n;
++ 
++       if (buf == NULL)
++         {
++           buflen = 8096;
++           buf = malloc (buflen);
++         }
++       buf[0] = '\0';
++       fgets (buf, buflen - 1, fp);
++       if (buf != NULL)
++         n = strlen (buf);
++       else
++         n = 0;
++ #endif /* HAVE_GETLINE / HAVE_GETDELIM */
++       cp = buf;
++ 
++       if (n < 1)
++         break;
++ 
++       tmp = strchr (cp, '#');  /* remove comments */
++       if (tmp)
++         *tmp = '\0';
++       while (isspace ((int)*cp))    /* remove spaces and tabs */
++         ++cp;
++       if (*cp == '\0')        /* ignore empty lines */
++         continue;
++ 
++       if (cp[strlen (cp) - 1] == '\n')
++         cp[strlen (cp) - 1] = '\0';
++ 
++       tmp = strsep (&cp, " \t=");
++       if (cp != NULL)
++ 	while (isspace ((int)*cp) || *cp == '=')
++ 	  ++cp;
++ 
++       store (tmp, cp);
++     }
++   fclose (fp);
++ 
++   if (buf)
++     free (buf);
++ }
++ 
++ static void
++ load_defaults (void)
++ {
++   load_defaults_internal ("/etc/default/su");
++   load_defaults_internal ("/etc/login.defs");
++ }
++ 
++ int
++ getdef_bool (const char *name, int dflt)
++ {
++   const char *val;
++ 
++   if (list == NULL)
++     load_defaults ();
++ 
++   val = search (name);
++ 
++   if (val == NULL)
++     return dflt;
++ 
++   return (strcasecmp (val, "yes") == 0);
++ }
++ 
++ long
++ getdef_num (const char *name, long dflt)
++ {
++   const char *val;
++   char *cp;
++   long retval;
++ 
++   if (list == NULL)
++     load_defaults ();
++ 
++   val = search (name);
++ 
++   if (val == NULL)
++     return dflt;
++ 
++   retval = strtol (val, &cp, 0);
++   if (*cp != '\0' ||
++       ((retval == LONG_MAX || retval == LONG_MIN) && errno == ERANGE))
++     {
++       fprintf (stderr,
++ 	       "%s contains invalid numerical value: %s!\n",
++ 	       name, val);
++       retval = dflt;
++     }
++   return retval;
++ }
++ 
++ unsigned long
++ getdef_unum (const char *name, unsigned long dflt)
++ {
++   const char *val;
++   char *cp;
++   unsigned long retval;
++ 
++   if (list == NULL)
++     load_defaults ();
++ 
++   val = search (name);
++ 
++   if (val == NULL)
++     return dflt;
++ 
++   retval = strtoul (val, &cp, 0);
++   if (*cp != '\0' || (retval == ULONG_MAX && errno == ERANGE))
++     {
++       fprintf (stderr,
++ 	       "%s contains invalid numerical value: %s!\n",
++ 	       name, val);
++       retval = dflt;
++     }
++   return retval;
++ }
++ 
++ const char *
++ getdef_str (const char *name, const char *dflt)
++ {
++   const char *retval;
++ 
++   if (list == NULL)
++     load_defaults ();
++ 
++   retval = search (name);
++ 
++   return retval ?: dflt;
++ }
++ 
++ #if defined(TEST)
++ 
++ int
++ main ()
++ {
++   printf ("CYPT=%s\n", getdef_str ("cRypt", "no"));
++   printf ("LOG_UNKFAIL_ENAB=%s\n", getdef_str ("log_unkfail_enab",""));
++   printf ("DOESNOTEXIST=%s\n", getdef_str ("DOESNOTEXIST","yes"));
++   return 0;
++ }
++ 
++ #endif
+diff -rcN coreutils-6.7-orig/src/getdef.h coreutils-6.7/src/getdef.h
+*** coreutils-6.7-orig/src/getdef.h	Thu Jan  1 00:00:00 1970
+--- coreutils-6.7/src/getdef.h	Tue Jan 16 22:18:57 2007
+***************
+*** 0 ****
+--- 1,29 ----
++ /* Copyright (C) 2003, 2005 Thorsten Kukuk
++    Author: Thorsten Kukuk <kukuk@suse.de>
++ 
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License version 2 as
++    published by the Free Software Foundation.
++ 
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++ 
++    You should have received a copy of the GNU General Public License
++    along with this program; if not, write to the Free Software Foundation,
++    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
++ 
++ #ifndef _GETDEF_H_
++ 
++ #define _GETDEF_H_ 1
++ 
++ extern int getdef_bool (const char *name, int dflt);
++ extern long getdef_num (const char *name, long dflt);
++ extern unsigned long getdef_unum (const char *name, unsigned long dflt);
++ extern const char *getdef_str (const char *name, const char *dflt);
++ 
++ /* Free all data allocated by getdef_* calls before.  */
++ extern void free_getdef_data (void);
++ 
++ #endif /* _GETDEF_H_ */
+diff -rcN coreutils-6.7-orig/src/su.c coreutils-6.7/src/su.c
+*** coreutils-6.7-orig/src/su.c	Sun Oct 22 16:54:15 2006
+--- coreutils-6.7/src/su.c	Tue Jan 16 22:19:02 2007
+***************
+*** 38,43 ****
+--- 38,49 ----
+     restricts who can su to UID 0 accounts.  RMS considers that to
+     be fascist.
+  
++    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.d/su
++ 
+     Compile-time options:
+     -DSYSLOG_SUCCESS	Log successful su's (by default, to root) with syslog.
+     -DSYSLOG_FAILURE	Log failed su's (by default, to root) with syslog.
+***************
+*** 53,58 ****
+--- 59,71 ----
+  #include <sys/types.h>
+  #include <pwd.h>
+  #include <grp.h>
++ #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
+  
+  /* Hide any system prototype for getusershell.
+     This is necessary because some Cray systems have a conflicting
+***************
+*** 66,71 ****
+--- 79,87 ----
+  
+  #if HAVE_SYSLOG_H && HAVE_SYSLOG
+  # include <syslog.h>
++ # define SYSLOG_SUCCESS  1
++ # define SYSLOG_FAILURE  1
++ # define SYSLOG_NON_ROOT 1
+  #else
+  # undef SYSLOG_SUCCESS
+  # undef SYSLOG_FAILURE
+***************
+*** 99,117 ****
+  # include <paths.h>
+  #endif
+  
+  /* The default PATH for simulated logins to non-superuser accounts.  */
+! #ifdef _PATH_DEFPATH
+! # define DEFAULT_LOGIN_PATH _PATH_DEFPATH
+! #else
+! # define DEFAULT_LOGIN_PATH ":/usr/ucb:/bin:/usr/bin"
+! #endif
+  
+  /* The default PATH for simulated logins to superuser accounts.  */
+! #ifdef _PATH_DEFPATH_ROOT
+! # define DEFAULT_ROOT_LOGIN_PATH _PATH_DEFPATH_ROOT
+! #else
+! # define DEFAULT_ROOT_LOGIN_PATH "/usr/ucb:/bin:/usr/bin:/etc"
+! #endif
+  
+  /* The shell to run if none is given in the user's passwd entry.  */
+  #define DEFAULT_SHELL "/bin/sh"
+--- 115,127 ----
+  # include <paths.h>
+  #endif
+  
++ #include "getdef.h"
++ 
+  /* The default PATH for simulated logins to non-superuser accounts.  */
+! #define DEFAULT_LOGIN_PATH "/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin"
+  
+  /* The default PATH for simulated logins to superuser accounts.  */
+! #define DEFAULT_ROOT_LOGIN_PATH "/usr/sbin:/bin:/usr/bin:/sbin:/usr/X11R6/bin"
+  
+  /* The shell to run if none is given in the user's passwd entry.  */
+  #define DEFAULT_SHELL "/bin/sh"
+***************
+*** 119,125 ****
+--- 129,137 ----
+  /* The user to become if none is specified.  */
+  #define DEFAULT_USER "root"
+  
++ #ifndef USE_PAM
+  char *crypt ();
++ #endif
+  char *getusershell ();
+  void endusershell ();
+  void setusershell ();
+***************
+*** 216,222 ****
+--- 228,253 ----
+  }
+  #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(a) \
++   if (retval) \
++     { \
++       pam_end (pamh, PAM_SUCCESS); \
++       a; \
++     }
++ #endif
++ 
+  /* Ask the user for a password.
++    If PAM is in use, let PAM ask for the password if necessary.
+     Return true if the user gives the correct password for entry PW,
+     false if not.  Return true without asking for a password if run by UID 0
+     or if PW has an empty password.  */
+***************
+*** 224,233 ****
+  static bool
+  correct_password (const struct passwd *pw)
+  {
+    char *unencrypted, *encrypted, *correct;
+  #if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP
+    /* Shadow passwd stuff for SVR3 and maybe other systems.  */
+!   struct spwd *sp = getspnam (pw->pw_name);
+  
+    endspent ();
+    if (sp)
+--- 255,303 ----
+  static bool
+  correct_password (const struct passwd *pw)
+  {
++ #ifdef USE_PAM
++   const struct passwd *lpw;
++   const char *cp;
++ 
++   retval = pam_start ("su", pw->pw_name, &conv, &pamh);
++   PAM_BAIL_P (return false);
++ 
++   if (isatty (0) && (cp = ttyname (0)) != NULL)
++     {
++       const char *tty;
++ 
++       if (strncmp (cp, "/dev/", 5) == 0)
++ 	tty = cp + 5;
++       else
++ 	tty = cp;
++       retval = pam_set_item (pamh, PAM_TTY, tty);
++       PAM_BAIL_P (return false);
++     }
++   cp = getlogin ();
++   if (!(cp && *cp && (lpw = getpwnam (cp)) != NULL && lpw->pw_uid == getuid ()))
++     lpw = getpwuid (getuid ());
++   if (lpw)
++     {
++       retval = pam_set_item (pamh, PAM_RUSER, (const void *) lpw->pw_name);
++       PAM_BAIL_P (return false);
++     }
++   retval = pam_authenticate (pamh, 0);
++   PAM_BAIL_P (return false);
++   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 (return false);
++     }
++   PAM_BAIL_P (return false);
++   /* must be authenticated if this point was reached */
++   return true;
++ #else /* !USE_PAM */
+    char *unencrypted, *encrypted, *correct;
+  #if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP
+    /* Shadow passwd stuff for SVR3 and maybe other systems.  */
+!   const struct spwd *sp = getspnam (pw->pw_name);
+  
+    endspent ();
+    if (sp)
+***************
+*** 248,253 ****
+--- 318,324 ----
+    encrypted = crypt (unencrypted, correct);
+    memset (unencrypted, 0, strlen (unencrypted));
+    return STREQ (encrypted, correct);
++ #endif /* !USE_PAM */
+  }
+  
+  /* Update `environ' for the new shell based on PW, with SHELL being
+***************
+*** 272,279 ****
+        xsetenv ("USER", pw->pw_name);
+        xsetenv ("LOGNAME", pw->pw_name);
+        xsetenv ("PATH", (pw->pw_uid
+! 			? DEFAULT_LOGIN_PATH
+! 			: DEFAULT_ROOT_LOGIN_PATH));
+      }
+    else
+      {
+--- 343,350 ----
+        xsetenv ("USER", pw->pw_name);
+        xsetenv ("LOGNAME", pw->pw_name);
+        xsetenv ("PATH", (pw->pw_uid
+! 			? getdef_str ("PATH", DEFAULT_LOGIN_PATH)
+! 			: getdef_str ("SUPATH", DEFAULT_ROOT_LOGIN_PATH)));
+      }
+    else
+      {
+***************
+*** 283,288 ****
+--- 354,365 ----
+  	{
+  	  xsetenv ("HOME", pw->pw_dir);
+  	  xsetenv ("SHELL", shell);
++ 	  if (getdef_bool ("ALWAYS_SET_PATH", 0))
++ 	    xsetenv ("PATH", (pw->pw_uid
++ 			      ? getdef_str ("PATH",
++ 					    DEFAULT_LOGIN_PATH)
++ 			      : getdef_str ("SUPATH",
++ 					    DEFAULT_ROOT_LOGIN_PATH)));
+  	  if (pw->pw_uid)
+  	    {
+  	      xsetenv ("USER", pw->pw_name);
+***************
+*** 303,314 ****
+--- 380,414 ----
+      error (EXIT_FAIL, errno, _("cannot set groups"));
+    endgrent ();
+  #endif
++ #ifdef USE_PAM
++   retval = pam_setcred (pamh, PAM_ESTABLISH_CRED);
++   if (retval != PAM_SUCCESS)
++     error (EXIT_FAIL, 0, "%s", pam_strerror (pamh, retval));
++ 
++   retval = pam_open_session (pamh,0);
++   if (retval != PAM_SUCCESS)
++     {
++        pam_setcred (pamh, PAM_DELETE_CRED);
++        error (EXIT_FAIL, 0, "could not open session: %s",
++               pam_strerror (pamh, retval));
++     }
++ #endif /* USE_PAM */
+    if (setgid (pw->pw_gid))
+      error (EXIT_FAIL, errno, _("cannot set group id"));
+    if (setuid (pw->pw_uid))
+      error (EXIT_FAIL, errno, _("cannot set user id"));
+  }
+  
++ #ifdef USE_PAM
++ static bool caught = false;
++ /* Signal handler for parent process later */
++ static void
++ su_catch_sig (int sig)
++ {
++   caught = true;
++ }
++ #endif
++ 
+  /* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
+     If COMMAND is nonzero, pass it to the shell with the -c option.
+     Pass ADDITIONAL_ARGS to the shell as more arguments; there
+***************
+*** 321,326 ****
+--- 421,523 ----
+    size_t n_args = 1 + fast_startup + 2 * !!command + n_additional_args + 1;
+    char const **args = xnmalloc (n_args, sizeof *args);
+    size_t argno = 1;
++ #ifdef USE_PAM
++   pid_t child;
++   sigset_t ourset;
++   int status;
++ 
++   child = fork ();
++   if (child == (pid_t) -1)
++     error (EXIT_FAILURE, errno, "cannot fork");
++ 
++   if (child != 0)
++     {
++       /* parent only */
++       sigfillset (&ourset);
++       if (sigprocmask (SIG_BLOCK, &ourset, NULL))
++ 	{
++ 	  error (0, errno, "cannot block signals");
++ 	  caught = true;
++ 	}
++       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))
++ 	    {
++ 	      error (0, errno, "cannot set signal handler");
++ 	      caught = true;
++ 	    }
++ 	}
++       if (!caught)
++ 	{
++ 	  for (;;)
++ 	    {
++ 	      pid_t pid;
++ 
++ 	      pid = waitpid (child, &status, WUNTRACED);
++ 
++ 	      if (WIFSTOPPED (status))
++ 		{
++ 		  kill (getpid (), SIGSTOP);
++ 		  /* once we get here, we must have resumed */
++ 		  kill (pid, SIGCONT);
++ 		}
++ 	      else
++ 		break;
++ 	    }
++ 	  if (WIFSIGNALED (status))
++ 	    status = WTERMSIG (status) + 128;
++ 	  else
++ 	    status = WEXITSTATUS (status);
++ 	}
++       else
++ 	status = 1;
++ 
++       if (caught)
++ 	{
++ 	  fprintf (stderr, "\nSession terminated, killing shell...");
++ 	  kill (child, SIGTERM);
++ 	}
++       retval = pam_setcred (pamh, PAM_DELETE_CRED);
++       PAM_BAIL_P (exit (EXIT_FAILURE));
++       retval = pam_close_session (pamh, 0);
++       PAM_BAIL_P (exit (EXIT_FAILURE));
++       retval = pam_end (pamh, PAM_SUCCESS);
++       PAM_BAIL_P (exit (EXIT_FAILURE));
++       if (caught)
++ 	{
++ 	  sleep (2);
++ 	  kill (child, SIGKILL);
++ 	  fprintf (stderr, " ...killed.\n");
++ 	}
++       exit (status);
++     }
++ 
++   /* child shell */
++ 
++   /* Export env variables declared by PAM modules */
++   {
++     const char *const *env;
++ 
++     env = (const char *const *) pam_getenvlist (pamh);
++     while (env && *env)
++       {
++ 	
++ 	if (putenv (*env) != 0)
++ 	  xalloc_die ();
++         env++;
++       }
++   }
++ 
++   pam_end (pamh, 0);
++ #endif
+  
+    if (simulate_login)
+      {
+***************
+*** 339,344 ****
+--- 536,546 ----
+      args[argno++] = "-f";
+    if (command)
+      {
++       if (simulate_login)
++ 	/* Bash 2.0 have to be invoked as `-su'.  See the comments in
++ 	   `shell.c (run_startup_files)'.  */
++ 	args[0] = "-su";
++ 
+        args[argno++] = "-c";
+        args[argno++] = command;
+      }
+***************
+*** 495,500 ****
+--- 697,705 ----
+  #ifdef SYSLOG_FAILURE
+        log_su (pw, false);
+  #endif
++ #ifdef USE_PAM
++       sleep (getdef_num ("FAIL_DELAY", 1));
++ #endif
+        error (EXIT_FAIL, 0, _("incorrect password"));
+      }
+  #ifdef SYSLOG_SUCCESS
+***************
+*** 516,524 ****
+        shell = NULL;
+      }
+    shell = xstrdup (shell ? shell : pw->pw_shell);
+    modify_environment (pw, shell);
+  
+-   change_identity (pw);
+    if (simulate_login && chdir (pw->pw_dir) != 0)
+      error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir);
+  
+--- 721,732 ----
+        shell = NULL;
+      }
+    shell = xstrdup (shell ? shell : pw->pw_shell);
++   change_identity (pw);
++ 
++   /* Set environment after pam_open_session, which may put KRB5CCNAME
++      into the pam_env, etc.  */
+    modify_environment (pw, shell);
+  
+    if (simulate_login && chdir (pw->pw_dir) != 0)
+      error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir);
+