d66c4db
--- coreutils-6.7/src/Makefile.am.pam	2006-11-24 21:28:10.000000000 +0000
d66c4db
+++ coreutils-6.7/src/Makefile.am	2007-01-09 17:00:01.000000000 +0000
d66c4db
@@ -103,7 +103,7 @@
d66c4db
 # If necessary, add -lm to resolve use of pow in lib/strtod.c.
d66c4db
 uptime_LDADD = $(LDADD) $(POW_LIB) $(GETLOADAVG_LIBS)
cvsdist 5adf0da
 
d66c4db
-su_LDADD = $(LDADD) $(LIB_CRYPT)
d66c4db
+su_LDADD = $(LDADD) $(LIB_CRYPT) @LIB_PAM@
cvsdist 5adf0da
 
d66c4db
 dir_LDADD += $(LIB_ACL)
d66c4db
 ls_LDADD += $(LIB_ACL)
d66c4db
--- coreutils-6.7/src/su.c.pam	2007-01-09 17:00:01.000000000 +0000
d66c4db
+++ coreutils-6.7/src/su.c	2007-01-09 17:16:43.000000000 +0000
cvsdist 5adf0da
@@ -38,6 +38,16 @@
cvsdist 5adf0da
    restricts who can su to UID 0 accounts.  RMS considers that to
cvsdist 5adf0da
    be fascist.
cvsdist 5adf0da
 
cvsdist 5adf0da
+#ifdef USE_PAM
cvsdist 5adf0da
+
cvsdist 5adf0da
+   Actually, with PAM, su has nothing to do with whether or not a
cvsdist 5adf0da
+   wheel group is enforced by su.  RMS tries to restrict your access
cvsdist 5adf0da
+   to a su which implements the wheel group, but PAM considers that
cvsdist 5adf0da
+   to be fascist, and gives the user/sysadmin the opportunity to
cvsdist 5adf0da
+   enforce a wheel group by proper editing of /etc/pam.conf
cvsdist 5adf0da
+
cvsdist 5adf0da
+#endif
cvsdist 5adf0da
+
d66c4db
    Compile-time options:
d66c4db
    -DSYSLOG_SUCCESS	Log successful su's (by default, to root) with syslog.
d66c4db
    -DSYSLOG_FAILURE	Log failed su's (by default, to root) with syslog.
d66c4db
@@ -59,6 +69,15 @@
cvsdist 5adf0da
    prototype (returning `int') in <unistd.h>.  */
cvsdist 5adf0da
 #define getusershell _getusershell_sys_proto_
cvsdist 5adf0da
 
cvsdist 5adf0da
+#ifdef USE_PAM
cvsdist 5adf0da
+# include <signal.h>
cvsdist 5adf0da
+# include <sys/wait.h>
cvsdist 5adf0da
+# include <sys/fsuid.h>
e87740c
+# include <unistd.h>
3dd4afb
+# include <security/pam_appl.h>
3dd4afb
+# include <security/pam_misc.h>
cvsdist 5adf0da
+#endif /* USE_PAM */
cvsdist 5adf0da
+
cvsdist 5adf0da
 #include "system.h"
d66c4db
 #include "getpass.h"
cvsdist 5adf0da
 
d66c4db
@@ -128,15 +147,22 @@
cvsdist 5adf0da
 /* The user to become if none is specified.  */
cvsdist 5adf0da
 #define DEFAULT_USER "root"
cvsdist 5adf0da
 
cvsdist 5adf0da
+#ifndef USE_PAM
c4b1fe4
 char *crypt (char const *key, char const *salt);
cvsdist 5adf0da
+#endif
c4b1fe4
 char *getusershell (void);
c4b1fe4
 void endusershell (void);
c4b1fe4
 void setusershell (void);
cvsdist 5adf0da
 
cvsdist 5adf0da
 extern char **environ;
cvsdist 5adf0da
 
c3e4c84
-static void run_shell (char const *, char const *, char **, size_t)
c3e4c84
+static void run_shell (char const *, char const *, char **, size_t,
c3e4c84
+	const struct passwd *)
cvsdist 5adf0da
+#ifdef USE_PAM
c3e4c84
+	;
cvsdist 5adf0da
+#else
cvsdist 5adf0da
      ATTRIBUTE_NORETURN;
cvsdist 5adf0da
+#endif
cvsdist 5adf0da
 
e18e415
 /* If true, pass the `-f' option to the subshell.  */
e18e415
 static bool fast_startup;
d66c4db
@@ -225,7 +251,26 @@
cvsdist 5adf0da
 }
cvsdist 5adf0da
 #endif
cvsdist 5adf0da
 
cvsdist 5adf0da
+#ifdef USE_PAM
cvsdist 5adf0da
+static pam_handle_t *pamh = NULL;
cvsdist 5adf0da
+static int retval;
cvsdist 5adf0da
+static struct pam_conv conv = {
cvsdist 5adf0da
+  misc_conv,
cvsdist 5adf0da
+  NULL
cvsdist 5adf0da
+};
cvsdist 5adf0da
+
cvsdist 5adf0da
+#define PAM_BAIL_P if (retval) { \
cvsdist 5adf0da
+  pam_end(pamh, PAM_SUCCESS); \
cvsdist 5adf0da
+  return 0; \
cvsdist 5adf0da
+}
ee719b5
+#define PAM_BAIL_P_VOID if (retval) {		\
ee719b5
+  pam_end(pamh, PAM_SUCCESS);			\
ee719b5
+return;						\
ee719b5
+}
cvsdist 5adf0da
+#endif
cvsdist 5adf0da
+
cvsdist 5adf0da
 /* Ask the user for a password.
cvsdist 5adf0da
+   If PAM is in use, let PAM ask for the password if necessary.
c3e4c84
    Return true if the user gives the correct password for entry PW,
c3e4c84
    false if not.  Return true without asking for a password if run by UID 0
cvsdist 5adf0da
    or if PW has an empty password.  */
d66c4db
@@ -233,6 +278,44 @@
c3e4c84
 static bool
cvsdist 5adf0da
 correct_password (const struct passwd *pw)
cvsdist 5adf0da
 {
cvsdist 5adf0da
+#ifdef USE_PAM
cvsdist 5adf0da
+  struct passwd *caller;
e87740c
+  char *tty_name, *ttyn;
cvsdist 5adf0da
+  retval = pam_start(PROGRAM_NAME, pw->pw_name, &conv, &pamh);
cvsdist 5adf0da
+  PAM_BAIL_P;
cvsdist 5adf0da
+
cvsdist 5adf0da
+  if (getuid() != 0 && !isatty(0)) {
cvsdist 5adf0da
+	fprintf(stderr, "standard in must be a tty\n");
cvsdist 5adf0da
+	exit(1);
cvsdist 5adf0da
+  }
cvsdist 5adf0da
+
cvsdist 5adf0da
+  caller = getpwuid(getuid());
cvsdist 5adf0da
+  if(caller != NULL && caller->pw_name != NULL) {
cvsdist 5adf0da
+	  retval = pam_set_item(pamh, PAM_RUSER, caller->pw_name);
cvsdist 5adf0da
+	  PAM_BAIL_P;
cvsdist 5adf0da
+  }
cvsdist 5adf0da
+
e87740c
+  ttyn = ttyname(0);
f1ce78f
+  if (ttyn) {
f1ce78f
+    if (strncmp(ttyn, "/dev/", 5) == 0)
e87740c
+       tty_name = ttyn+5;
f1ce78f
+    else
e87740c
+       tty_name = ttyn;
f1ce78f
+    retval = pam_set_item(pamh, PAM_TTY, tty_name);
f1ce78f
+    PAM_BAIL_P;
f1ce78f
+  }
cvsdist 5adf0da
+  retval = pam_authenticate(pamh, 0);
cvsdist 5adf0da
+  PAM_BAIL_P;
cvsdist 5adf0da
+  retval = pam_acct_mgmt(pamh, 0);
cvsdist 5adf0da
+  if (retval == PAM_NEW_AUTHTOK_REQD) {
cvsdist 5adf0da
+    /* password has expired.  Offer option to change it. */
cvsdist 5adf0da
+    retval = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
cvsdist 5adf0da
+    PAM_BAIL_P;
cvsdist 5adf0da
+  }
cvsdist 5adf0da
+  PAM_BAIL_P;
cvsdist 5adf0da
+  /* must be authenticated if this point was reached */
cvsdist 5adf0da
+  return 1;
cvsdist 5adf0da
+#else /* !USE_PAM */
cvsdist 5adf0da
   char *unencrypted, *encrypted, *correct;
cvsdist 5adf0da
 #if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP
cvsdist 5adf0da
   /* Shadow passwd stuff for SVR3 and maybe other systems.  */
d66c4db
@@ -257,6 +340,7 @@
cvsdist 5adf0da
   encrypted = crypt (unencrypted, correct);
cvsdist 5adf0da
   memset (unencrypted, 0, strlen (unencrypted));
c3e4c84
   return STREQ (encrypted, correct);
cvsdist 5adf0da
+#endif /* !USE_PAM */
cvsdist 5adf0da
 }
cvsdist 5adf0da
 
cvsdist 5adf0da
 /* Update `environ' for the new shell based on PW, with SHELL being
d66c4db
@@ -270,12 +354,18 @@
c3e4c84
       /* Leave TERM unchanged.  Set HOME, SHELL, USER, LOGNAME, PATH.
cvsdist 5adf0da
          Unset all other environment variables.  */
c3e4c84
       char const *term = getenv ("TERM");
c3e4c84
+      char const *display = getenv ("DISPLAY");
c3e4c84
+      char const *xauthority = getenv ("XAUTHORITY");
c3e4c84
       if (term)
2505178
         term = xstrdup (term);
c3e4c84
       environ = xmalloc ((6 + !!term) * sizeof (char *));
c3e4c84
       environ[0] = NULL;
cvsdist 5adf0da
       if (term)
2505178
         xsetenv ("TERM", term);
cvsdist 5adf0da
+      if (display)
2505178
+        xsetenv ("DISPLAY", display);
cvsdist 5adf0da
+      if (xauthority)
2505178
+        xsetenv ("XAUTHORITY", xauthority);
c3e4c84
       xsetenv ("HOME", pw->pw_dir);
c3e4c84
       xsetenv ("SHELL", shell);
c3e4c84
       xsetenv ("USER", pw->pw_name);
d66c4db
@@ -308,8 +398,13 @@
e87740c
 {
e87740c
 #ifdef HAVE_INITGROUPS
e87740c
   errno = 0;
e87740c
-  if (initgroups (pw->pw_name, pw->pw_gid) == -1)
e87740c
+  if (initgroups (pw->pw_name, pw->pw_gid) == -1) {
e87740c
+#ifdef USE_PAM
e87740c
+    pam_close_session(pamh, 0);
e87740c
+    pam_end(pamh, PAM_ABORT);
e87740c
+#endif
0363d66
     error (EXIT_FAILURE, errno, _("cannot set groups"));
e87740c
+  }
cvsdist 5adf0da
   endgrent ();
cvsdist 5adf0da
 #endif
cvsdist 5adf0da
   if (setgid (pw->pw_gid))
d66c4db
@@ -318,6 +413,31 @@
0363d66
     error (EXIT_FAILURE, errno, _("cannot set user id"));
cvsdist 5adf0da
 }
cvsdist 5adf0da
 
cvsdist 5adf0da
+#ifdef USE_PAM
cvsdist 5adf0da
+static int caught=0;
cvsdist 5adf0da
+/* Signal handler for parent process later */
cvsdist 5adf0da
+static void su_catch_sig(int sig)
cvsdist 5adf0da
+{
cvsdist 5adf0da
+  ++caught;
cvsdist 5adf0da
+}
cvsdist 5adf0da
+
cvsdist 5adf0da
+int
cvsdist 5adf0da
+pam_copyenv (pam_handle_t *pamh)
cvsdist 5adf0da
+{
cvsdist 5adf0da
+  char **env;
cvsdist 5adf0da
+
cvsdist 5adf0da
+  env = pam_getenvlist(pamh);
cvsdist 5adf0da
+  if(env) {
cvsdist 5adf0da
+    while(*env) {
c3e4c84
+	if (putenv (*env))
c3e4c84
+	  xalloc_die ();
cvsdist 5adf0da
+	env++;
cvsdist 5adf0da
+    }
cvsdist 5adf0da
+  }
cvsdist 5adf0da
+  return(0);
cvsdist 5adf0da
+}
cvsdist 5adf0da
+#endif
cvsdist 5adf0da
+
cvsdist 5adf0da
 /* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
cvsdist 5adf0da
    If COMMAND is nonzero, pass it to the shell with the -c option.
c3e4c84
    Pass ADDITIONAL_ARGS to the shell as more arguments; there
d66c4db
@@ -325,17 +445,49 @@
cvsdist 5adf0da
 
cvsdist 5adf0da
 static void
c3e4c84
 run_shell (char const *shell, char const *command, char **additional_args,
2505178
-           size_t n_additional_args)
2505178
+           size_t n_additional_args, const struct passwd *pw)
cvsdist 5adf0da
 {
c3e4c84
   size_t n_args = 1 + fast_startup + 2 * !!command + n_additional_args + 1;
c3e4c84
   char const **args = xnmalloc (n_args, sizeof *args);
c3e4c84
   size_t argno = 1;
cvsdist 5adf0da
+#ifdef USE_PAM
cvsdist 5adf0da
+  int child;
cvsdist 5adf0da
+  sigset_t ourset;
cvsdist 5adf0da
+  int status;
cvsdist 5adf0da
+
cvsdist 5adf0da
+  retval = pam_open_session(pamh,0);
cvsdist 5adf0da
+  if (retval != PAM_SUCCESS) {
cvsdist 5adf0da
+    fprintf (stderr, "could not open session\n");
cvsdist 5adf0da
+    exit (1);
cvsdist 5adf0da
+  }
cvsdist 5adf0da
+
cvsdist 5adf0da
+/* do this at the last possible moment, because environment variables may
cvsdist 5adf0da
+   be passed even in the session phase
cvsdist 5adf0da
+*/
cvsdist 5adf0da
+  if(pam_copyenv(pamh) != PAM_SUCCESS)
cvsdist 5adf0da
+     fprintf (stderr, "error copying PAM environment\n");
cvsdist 5adf0da
+  
e87740c
+  /* Credentials should be set in the parent */ 
e87740c
+  if (pam_setcred(pamh, PAM_ESTABLISH_CRED) != PAM_SUCCESS) {
e87740c
+    pam_close_session(pamh, 0);
e87740c
+    fprintf(stderr, "could not set PAM credentials\n");
e87740c
+    exit(1);
e87740c
+  }
e87740c
+
cvsdist 5adf0da
+  child = fork();
cvsdist 5adf0da
+  if (child == 0) {  /* child shell */
cvsdist 5adf0da
+  change_identity (pw);
cvsdist 5adf0da
+  pam_end(pamh, 0);
cvsdist 5adf0da
+#endif
cvsdist 5adf0da
 
c3e4c84
   if (simulate_login)
c3e4c84
     {
cvsdist 5adf0da
       char *arg0;
cvsdist 5adf0da
       char *shell_basename;
cvsdist 5adf0da
 
cvsdist 5adf0da
+      if(chdir(pw->pw_dir))
cvsdist 5adf0da
+	      error(0, errno, _("warning: cannot change directory to %s"), pw->pw_dir);
cvsdist 5adf0da
+
d66c4db
       shell_basename = last_component (shell);
cvsdist 5adf0da
       arg0 = xmalloc (strlen (shell_basename) + 2);
cvsdist 5adf0da
       arg0[0] = '-';
d66c4db
@@ -360,6 +512,66 @@
cvsdist 5adf0da
     error (0, errno, "%s", shell);
cvsdist 5adf0da
     exit (exit_status);
cvsdist 5adf0da
   }
cvsdist 5adf0da
+#ifdef USE_PAM
cvsdist 5adf0da
+  } else if (child == -1) {
cvsdist 5adf0da
+      fprintf(stderr, "can not fork user shell: %s", strerror(errno));
e87740c
+      pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT);
e87740c
+      pam_close_session(pamh, 0);
e87740c
+      pam_end(pamh, PAM_ABORT);
cvsdist 5adf0da
+      exit(1);
cvsdist 5adf0da
+  }
cvsdist 5adf0da
+  /* parent only */
cvsdist 5adf0da
+  sigfillset(&ourset);
cvsdist 5adf0da
+  if (sigprocmask(SIG_BLOCK, &ourset, NULL)) {
cvsdist 5adf0da
+    fprintf(stderr, "%s: signal malfunction\n", PROGRAM_NAME);
cvsdist 5adf0da
+    caught = 1;
cvsdist 5adf0da
+  }
cvsdist 5adf0da
+  if (!caught) {
cvsdist 5adf0da
+    struct sigaction action;
cvsdist 5adf0da
+    action.sa_handler = su_catch_sig;
cvsdist 5adf0da
+    sigemptyset(&action.sa_mask);
cvsdist 5adf0da
+    action.sa_flags = 0;
cvsdist 5adf0da
+    sigemptyset(&ourset);
cvsdist 5adf0da
+    if (sigaddset(&ourset, SIGTERM)
cvsdist 5adf0da
+        || sigaddset(&ourset, SIGALRM)
cvsdist 5adf0da
+        || sigaction(SIGTERM, &action, NULL)
cvsdist 5adf0da
+        || sigprocmask(SIG_UNBLOCK, &ourset, NULL)) {
cvsdist 5adf0da
+      fprintf(stderr, "%s: signal masking malfunction\n", PROGRAM_NAME);
cvsdist 5adf0da
+      caught = 1;
cvsdist 5adf0da
+    }
cvsdist 5adf0da
+  }
cvsdist 5adf0da
+  if (!caught) {
cvsdist 5adf0da
+    do {
cvsdist 5adf0da
+      int pid;
cvsdist 5adf0da
+
cvsdist 5adf0da
+      pid = waitpid(-1, &status, WUNTRACED);
cvsdist 5adf0da
+
cvsdist 5adf0da
+      if (WIFSTOPPED(status)) {
cvsdist 5adf0da
+          kill(getpid(), SIGSTOP);
cvsdist 5adf0da
+          /* once we get here, we must have resumed */
cvsdist 5adf0da
+          kill(pid, SIGCONT);
cvsdist 5adf0da
+      }
cvsdist 5adf0da
+    } while (WIFSTOPPED(status));
cvsdist 5adf0da
+  }
cvsdist 5adf0da
+
cvsdist 5adf0da
+  if (caught) {
cvsdist 5adf0da
+    fprintf(stderr, "\nSession terminated, killing shell...");
cvsdist 5adf0da
+    kill (child, SIGTERM);
cvsdist 5adf0da
+  }
e87740c
+  /* Not checking retval on this because we need to call close session */
e87740c
+  pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT);
cvsdist 5adf0da
+  retval = pam_close_session(pamh, 0);
ee719b5
+  PAM_BAIL_P_VOID;
cvsdist 5adf0da
+  retval = pam_end(pamh, PAM_SUCCESS);
ee719b5
+  PAM_BAIL_P_VOID;
cvsdist 5adf0da
+  if (caught) {
cvsdist 5adf0da
+    sleep(2);
cvsdist 5adf0da
+    kill(child, SIGKILL);
cvsdist 5adf0da
+    fprintf(stderr, " ...killed.\n");
cvsdist 5adf0da
+    exit(-1);
cvsdist 5adf0da
+  }
cvsdist 5adf0da
+  exit (WEXITSTATUS(status));
cvsdist 5adf0da
+#endif /* USE_PAM */
cvsdist 5adf0da
 }
cvsdist 5adf0da
 
c3e4c84
 /* Return true if SHELL is a restricted shell (one not returned by
d66c4db
@@ -527,9 +739,9 @@
d66c4db
   shell = xstrdup (shell ? shell : pw->pw_shell);
d66c4db
   modify_environment (pw, shell);
cvsdist 5adf0da
 
a2135bf
+#ifndef USE_PAM
cvsdist 5adf0da
   change_identity (pw);
e6a6605
-  if (simulate_login && chdir (pw->pw_dir) != 0)
e6a6605
-    error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir);
cvsdist 5adf0da
+#endif
cvsdist 5adf0da
 
c3e4c84
-  run_shell (shell, command, argv + optind, MAX (0, argc - optind));
c3e4c84
+  run_shell (shell, command, argv + optind, MAX (0, argc - optind), pw);
cvsdist 5adf0da
 }
d66c4db
--- coreutils-6.7/doc/coreutils.texi.pam	2006-10-27 15:30:48.000000000 +0100
d66c4db
+++ coreutils-6.7/doc/coreutils.texi	2007-01-09 17:00:01.000000000 +0000
d66c4db
@@ -13395,8 +13395,11 @@
d66c4db
 @findex syslog
d66c4db
 @command{su} can optionally be compiled to use @code{syslog} to report
d66c4db
 failed, and optionally successful, @command{su} attempts.  (If the system
d66c4db
-supports @code{syslog}.)  However, GNU @command{su} does not check if the
d66c4db
-user is a member of the @code{wheel} group; see below.
d66c4db
+supports @code{syslog}.)
d66c4db
+
d66c4db
+This version of @command{su} has support for using PAM for
d66c4db
+authentication.  You can edit @file{/etc/pam.d/su} to customize its
d66c4db
+behaviour.
cvsdist 460c938
 
d66c4db
 The program accepts the following options.  Also see @ref{Common options}.
cvsdist 460c938
 
a7cab66
@@ -12815,6 +12815,8 @@
a7cab66
 @env{PATH} to a compiled-in default value.  Change to @var{user}'s home
a7cab66
 directory.  Prepend @samp{-} to the shell's name, intended to make it
a7cab66
 read its login startup file(s).
a7cab66
+Additionaly @env{DISPLAY} and @env{XAUTHORITY} environment variables 
a7cab66
+are preserved as well for PAM functionality.
a7cab66
a7cab66
 @item -m
a7cab66
 @itemx -p
d66c4db
@@ -13477,33 +13480,6 @@
d66c4db
 the exit status of the subshell otherwise
d66c4db
 @end display
cvsdist 460c938
 
d66c4db
-@cindex wheel group, not supported
d66c4db
-@cindex group wheel, not supported
d66c4db
-@cindex fascism
d66c4db
-@subsection Why GNU @command{su} does not support the @samp{wheel} group
d66c4db
-
d66c4db
-(This section is by Richard Stallman.)
d66c4db
-
d66c4db
-@cindex Twenex
d66c4db
-@cindex MIT AI lab
d66c4db
-Sometimes a few of the users try to hold total power over all the
d66c4db
-rest.  For example, in 1984, a few users at the MIT AI lab decided to
d66c4db
-seize power by changing the operator password on the Twenex system and
d66c4db
-keeping it secret from everyone else.  (I was able to thwart this coup
d66c4db
-and give power back to the users by patching the kernel, but I
d66c4db
-wouldn't know how to do that in Unix.)
d66c4db
-
d66c4db
-However, occasionally the rulers do tell someone.  Under the usual
d66c4db
-@command{su} mechanism, once someone learns the root password who
d66c4db
-sympathizes with the ordinary users, he or she can tell the rest.  The
d66c4db
-``wheel group'' feature would make this impossible, and thus cement the
d66c4db
-power of the rulers.
d66c4db
-
d66c4db
-I'm on the side of the masses, not that of the rulers.  If you are
d66c4db
-used to supporting the bosses and sysadmins in whatever they do, you
d66c4db
-might find this idea strange at first.
d66c4db
-
d66c4db
-
e18e415
 @node timeout invocation
e18e415
 @section @command{timeout}: Run a command with a time limit
cvsdist 460c938
 
4de88fb
--- coreutils-7.1/configure.ac.pam
4de88fb
+++ coreutils-7.1/configure.ac
0363d66
@@ -44,6 +44,13 @@
1ef0ec6
   AC_DEFINE([GNULIB_PORTCHECK], [1], [enable some gnulib portability checks])
4de88fb
 fi
e87740c
 
e87740c
+dnl Give the chance to enable PAM
e87740c
+AC_ARG_ENABLE(pam, dnl
e87740c
+[  --enable-pam              Enable use of the PAM libraries],
e87740c
+[AC_DEFINE(USE_PAM, 1, [Define if you want to use PAM])
e87740c
+LIB_PAM="-ldl -lpam -lpam_misc"
e87740c
+AC_SUBST(LIB_PAM)])
e87740c
+
0363d66
 AC_FUNC_FORK
0363d66
0363d66
 optional_bin_progs=